如果您想按“页码”进行分页,那么在您对键上的结果进行排序后,您几乎会被.limit() 和.skip() 方法所困扰。您可能已经阅读并发现它“效率不高”,主要是由于“跳过”“n”个结果以到达某个页面的成本。
但原则在你需要的地方是合理的:
db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>)
如果您只需要在分页中“向前”移动,则可以使用更快的替代方法,也可以用于“排序”结果。
关键是保持对“s”的“最后一次看到”值的引用,然后通常是_id值的列表,直到“s”的值发生变化。所以用更多的文档进行演示,为了演示目的已经排序:
{ "_id": 1, "s": 3 },
{ "_id": 2, "s": 3 },
{ "_id": 3, "s": 3 },
{ "_id": 4, "s": 2 },
{ "_id": 5, "s": 1 },
{ "_id": 6, "s": 1 },
为了获得“两个”结果的“第一页”,您的第一个查询很简单:
db.collection.find().sort({ "s": -1, "_id": 1}).limit(2)
但在处理文档时要遵循这一点:
var lastVal = null,
lastSeen = [];
db.collection.find().sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
if ( doc.s != lastVal ) { // Change when different
lastVal = doc.s;
lastSeen = [];
}
lastSeen.push(doc._id); // Push _id onto array
// do other things like output
})
因此,在第一次迭代中,lastVal 的值将是 3,lastSeen 将包含数组 [1,2] 中的两个文档 _id 值。
您可以将这些内容存储在等待下一页请求的用户会话数据中。
根据您对下一页设置的请求,您发出以下命令:
var lastVal = 3,
lastSeen = [1,2];
db.collection.find({
"_id": { "$nin": lastSeen },
"s": { "$lte": lastVal }
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
if ( doc.s != lastVal ) { // Change when different
lastVal = doc.s;
lastSeen = [];
}
lastSeen.push(doc._id); // Push _id onto array
// do other things like output
})
这要求“s”的选择都需要从记录的lastVal的值“小于或等于”(由于排序的方向)开始,并且“_id”字段不能包含lastSeen中记录的值。
生成的下一页是:
{ "_id": 3, "s": 3 },
{ "_id": 4, "s": 2 },
但是现在如果你按照逻辑lastVal 当然是2 而lastSeen 现在只有一个数组元素[4]。由于下一个查询只需要从 2 开始作为一个小于或等于的值,因此无需保留其他先前看到的“_id”值,因为它们不会在该选择范围内。
然后这个过程就开始了:
var lastVal = 2,
lastSeen = [2];
db.collection.find({
"_id": { "$nin": lastSeen },
"s": { "$lte": lastVal }
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
if ( doc.s != lastVal ) { // Change when different
lastVal = doc.s;
lastSeen = [];
}
lastSeen.push(doc._id); // Push _id onto array
// do other things like output
})
因此,通过遵循这种逻辑模式,您可以“存储”从结果的“上一页”中找到的信息,并非常有效地在结果中“前进”。
但如果您需要跳转到“第 20 页”或类似类型的操作,那么您将被 .limit() 和 .skip() 卡住。那样会慢一些,但这取决于你能忍受什么。