这称为“前向分页”,这是一个概念,您可以在使用“排序”结果时沿“前向”方向“有效地分页”结果。
包含 JavaScript 逻辑(因为它在 shell 中工作),但并不难翻译。
一般概念:
{ "_id": 1, "a": 3 },
{ "_id": 2, "a": 3 },
{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },
{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }
将那些“已排序”文档(为了方便起见)视为我们希望每页按“两个”项目“分页”的结果示例。
在第一种情况下,你会做这样的事情:
var lastVal = null,
lastSeen = [];
db.collection.find().sort({ "a": -1 }).limit(2).forEach(function(doc) {
if ( lastVal != doc.a ) {
lastSeen = [];
}
lastVal = doc.a;
lastSeen.push( doc._id );
// do something useful with each document matched
});
现在那些lastVal
and lastSeen
是存储在“会话变量”之类的东西中,可以在 Web 应用程序的下一个请求中访问,或者是其他类似的东西。
它们应该包含的是您排序的最后一个值和“唯一”列表_id
由于该值没有改变而看到的值。因此:
lastVal = 3,
lastSeen = [1,2];
关键是,当出现“下一页”的请求时,您希望将这些变量用于类似这样的事情:
var lastVal = 3,
lastSeen = [1,2];
db.collection.find({
"_id": { "$nin": lastSeen },
"a": { "$lte": lastVal }
}).sort({ "a": -1 }).limit(2).forEach(function(doc) {
if ( lastVal != doc.a ) {
lastSeen = [];
}
lastVal = doc.a;
lastSeen.push( doc._id );
// do something useful with each document matched
});
其作用是“排除”所有值_id
记录在lastSeen
从结果列表中,并确保所有结果都需要“小于或等于”(降序排列)lastVal
记录排序字段“a”。
这会在集合中产生接下来的两个结果:
{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },
但处理后我们的值现在看起来像这样:
lastVal = 2,
lastSeen = [4];
所以现在的逻辑是你不需要排除其他_id
之前看到的值,因为您实际上只是在寻找“a”的值,而不是“小于或等于”lastVal
因为只有“一个”_id
在该值处看到的值则仅排除该值。
这当然会产生使用与上面相同的代码的下一页:
{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }
一般来说,这是通过结果“转发页面”的最有效方式,并且对于“排序”结果的高效分页特别有用。
但是,如果您想“跳转”到页面20
或任何阶段的类似行为,那么这不适合您。你被传统束缚了.skip()
and .limit()
方法能够通过“页码”来做到这一点,因为没有其他合理的方法来“计算”这一点。
因此,这完全取决于您的应用程序如何实现“分页”以及您可以接受的内容。这.skip()
and .limit()
方法会受到“跳过”性能的影响,可以通过使用此处的方法来避免。
另一方面,如果您想要“跳转到页面”,那么“跳过”是您唯一真正的选择,除非您想构建结果的“缓存”。但这完全是另一个问题。