对集合进行排序和分页

And*_*ine 4 sorting pagination mongodb

如何对按非唯一字段排序的查询进行分页?例如,集合中的文档可能是(按s:1排序,然后是_id:-1):

{_id: 19, s: 3},
{_id: 17, s: 3},
{_id: 58, s: 4},
// etc...
Run Code Online (Sandbox Code Playgroud)

有一个简单的限制/跳过方法可以......慢慢地工作.

是否可以使用以下内容:

db.collection.find()
  .sort({s:1, _id:-1})
  .min({s:3, _id:17})    // this does not work as wanted!
  .limit(2);
Run Code Online (Sandbox Code Playgroud)

检索

{_id: 17, s: 3},
{_id: 58, s: 4}
Run Code Online (Sandbox Code Playgroud)

Bla*_*ven 7

如果您想通过"页码"进行分页那么你是非常坚持的.limit().skip()后你对你的关键结果进行排序采用的方法.您可能已经做了一些阅读并发现它"效率不高",主要是因为"跳过""n"结果以达到某个页面的成本.

但是你需要它的原则是合理的:

db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>)
Run Code Online (Sandbox Code Playgroud)

如果您只需要在分页中"向前"移动,则可以使用更快的替代方法,也可以使用"已排序"的结果.

关键是要保持对"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 },
Run Code Online (Sandbox Code Playgroud)

为了获得"两个"结果的"第一页",您的第一个查询很简单:

db.collection.find().sort({ "s": -1, "_id": 1}).limit(2)
Run Code Online (Sandbox Code Playgroud)

但是按照这样处理文件:

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
})
Run Code Online (Sandbox Code Playgroud)

因此,在第一次迭代时,lastVal值将是3,并且lastSeen将包含_id数组中的两个文档值[1,2].这些东西你会存储在等待下一页请求的用户会话数据中.

根据您对下一页设置的请求,您发出如下:

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
})
Run Code Online (Sandbox Code Playgroud)

这要求"s"的选择需要从记录的"小于或等于"(因为排序的方向)值开始lastVal,并且"_id"字段不能包含记录的值lastSeen.

生成的下一页是:

{ "_id": 3, "s": 3 },
{ "_id": 4, "s": 2 },
Run Code Online (Sandbox Code Playgroud)

但是现在,如果你遵循的逻辑lastVal当然是2lastSeen现在只有单一的数组元素[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
})
Run Code Online (Sandbox Code Playgroud)

因此,通过遵循这种逻辑模式,您可以"存储"从结果的"previousc页面"中找到的信息,并非常有效地"向前"通过结果.

但是如果你需要跳转到"第20页"或类似的操作类型,那么你就会被困在.limit().skip().它的速度较慢,但​​这取决于你可以忍受的东西.