在 mongodb 中实现分页
Implementing pagination in mongodb
我知道使用 skip
来实现分页是一种不好的做法,因为当您的数据变大时 skip
开始消耗大量内存。克服这个麻烦的一种方法是通过 _id
字段使用自然顺序:
//Page 1
db.users.find().limit(pageSize);
//Find the id of the last document in this page
last_id = ...
//Page 2
users = db.users.find({'_id'> last_id}). limit(10);
问题是 - 我是 mongo 的新手,不知道什么是最好的方法 last_id
你说的这个概念可以叫做"forward paging"。这样做的一个很好的理由与使用 .skip()
和 .limit()
修饰符不同,这不能用于 "go back" 到前一页或 "skip" 到特定页面。至少不需要花费大量精力来存储 "seen" 或 "discovered" 页面,所以如果那种类型的 "links to page" 分页是你想要的,那么你最好坚持使用 .skip()
和 .limit()
方法,尽管存在性能缺陷。
如果仅 "move forward" 对您来说是一个可行的选择,那么这里是基本概念:
db.junk.find().limit(3)
{ "_id" : ObjectId("54c03f0c2f63310180151877"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f63310180151878"), "a" : 4, "b" : 4 }
{ "_id" : ObjectId("54c03f0c2f63310180151879"), "a" : 10, "b" : 10 }
当然,这是您的第一页,最多 3 个项目。考虑现在用代码迭代游标:
var lastSeen = null;
var cursor = db.junk.find().limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
这样迭代游标并做一些事情,当到达游标中的最后一项为真时,您将 lastSeen
值存储到现在 _id
:
ObjectId("54c03f0c2f63310180151879")
在您随后的迭代中,您只需将您保留的 _id
值(在会话中或其他)提供给查询:
var cursor = db.junk.find({ "_id": { "$gt": lastSeen } }).limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
{ "_id" : ObjectId("54c03f0c2f6331018015187a"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f6331018015187b"), "a" : 6, "b" : 6 }
{ "_id" : ObjectId("54c03f0c2f6331018015187c"), "a" : 7, "b" : 7 }
这个过程不断重复,直到再也得不到结果。
这是 _id
等自然顺序的基本过程。对于其他事情,它会变得有点复杂。考虑以下因素:
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
要将其拆分为按排名排序的两个页面,那么您本质上需要知道的是您拥有什么 "already seen" 并排除这些结果。所以看第一页:
var lastSeen = null;
var seenIds = [];
var cursor = db.junk.find().sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
在下一次迭代中,您希望小于或等于 lastSeen "rank" 分数,但也排除那些已经看到的文档。您使用 $nin
运算符执行此操作:
var cursor = db.junk.find(
{ "_id": { "$nin": seenIds }, "rank": "$lte": lastSeen }
).sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
您实际持有多少 "seenIds" 取决于 "granular" 您的结果是该值可能发生变化的地方。在这种情况下,您可以检查当前 "rank" 分数是否不等于 lastSeen
值并丢弃当前 seenIds
内容,使其不会增长太多。
这就是 "forward paging" 的基本概念,供您练习和学习。
在MongoDB
中实现分页的最简单方法
// Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
query = query.skip(startIndex).limit(limit);
我知道使用 skip
来实现分页是一种不好的做法,因为当您的数据变大时 skip
开始消耗大量内存。克服这个麻烦的一种方法是通过 _id
字段使用自然顺序:
//Page 1
db.users.find().limit(pageSize);
//Find the id of the last document in this page
last_id = ...
//Page 2
users = db.users.find({'_id'> last_id}). limit(10);
问题是 - 我是 mongo 的新手,不知道什么是最好的方法 last_id
你说的这个概念可以叫做"forward paging"。这样做的一个很好的理由与使用 .skip()
和 .limit()
修饰符不同,这不能用于 "go back" 到前一页或 "skip" 到特定页面。至少不需要花费大量精力来存储 "seen" 或 "discovered" 页面,所以如果那种类型的 "links to page" 分页是你想要的,那么你最好坚持使用 .skip()
和 .limit()
方法,尽管存在性能缺陷。
如果仅 "move forward" 对您来说是一个可行的选择,那么这里是基本概念:
db.junk.find().limit(3)
{ "_id" : ObjectId("54c03f0c2f63310180151877"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f63310180151878"), "a" : 4, "b" : 4 }
{ "_id" : ObjectId("54c03f0c2f63310180151879"), "a" : 10, "b" : 10 }
当然,这是您的第一页,最多 3 个项目。考虑现在用代码迭代游标:
var lastSeen = null;
var cursor = db.junk.find().limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
这样迭代游标并做一些事情,当到达游标中的最后一项为真时,您将 lastSeen
值存储到现在 _id
:
ObjectId("54c03f0c2f63310180151879")
在您随后的迭代中,您只需将您保留的 _id
值(在会话中或其他)提供给查询:
var cursor = db.junk.find({ "_id": { "$gt": lastSeen } }).limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
{ "_id" : ObjectId("54c03f0c2f6331018015187a"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f6331018015187b"), "a" : 6, "b" : 6 }
{ "_id" : ObjectId("54c03f0c2f6331018015187c"), "a" : 7, "b" : 7 }
这个过程不断重复,直到再也得不到结果。
这是 _id
等自然顺序的基本过程。对于其他事情,它会变得有点复杂。考虑以下因素:
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
要将其拆分为按排名排序的两个页面,那么您本质上需要知道的是您拥有什么 "already seen" 并排除这些结果。所以看第一页:
var lastSeen = null;
var seenIds = [];
var cursor = db.junk.find().sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
在下一次迭代中,您希望小于或等于 lastSeen "rank" 分数,但也排除那些已经看到的文档。您使用 $nin
运算符执行此操作:
var cursor = db.junk.find(
{ "_id": { "$nin": seenIds }, "rank": "$lte": lastSeen }
).sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
您实际持有多少 "seenIds" 取决于 "granular" 您的结果是该值可能发生变化的地方。在这种情况下,您可以检查当前 "rank" 分数是否不等于 lastSeen
值并丢弃当前 seenIds
内容,使其不会增长太多。
这就是 "forward paging" 的基本概念,供您练习和学习。
在MongoDB
中实现分页的最简单方法 // Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
query = query.skip(startIndex).limit(limit);