在mongodb中实现分页

Jac*_*ian 8 javascript pagination mongodb mongodb-query

我知道使用它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);
Run Code Online (Sandbox Code Playgroud)

问题是 - 我是mongo的新手,不知道最好的方法是什么 last_id

Nei*_*unn 23

您正在谈论的概念可以称为"前向寻呼".一个很好的理由不同于使用.skip().limit()修饰符,这不能用于"返回"前一页或实际上"跳过"到特定页面.至少没有大量的努力来存储"看到"或"发现"的页面,所以如果你想要的那种"链接页面"分页,那么你最好坚持使用.skip().limit()方法,尽管性能缺点.

如果你只是"前进"是一个可行的选择,那么这里是基本概念:

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

当然这是你的第一页,限制为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;
}
Run Code Online (Sandbox Code Playgroud)

所以,遍历该光标并做一些事情,当它是真实的,在光标的最后一个项目达到您存储lastSeen值本_id:

ObjectId("54c03f0c2f63310180151879")
Run Code Online (Sandbox Code Playgroud)

在随后的迭代中,您只需_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 }
Run Code Online (Sandbox Code Playgroud)

并且该过程反复重复直到不能获得更多结果.

这是自然秩序的基本过程,如_id.对于其他东西,它会变得更复杂.考虑以下:

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

要拆分成等级排序的两个页面,那么你基本上是需要知道的是你"已经看到"什么,排除那些结果.所以看第一页:

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

在下一次迭代中,您希望小于或等于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 }
Run Code Online (Sandbox Code Playgroud)

您实际持有多少"seenIds"取决于您的结果在该值可能发生变化的地方的"细化"程度.在这种情况下,您可以检查当前"等级"分数是否不等于该lastSeen值并丢弃当前seenIds内容,以使其不会增长太多.

这是"前向寻呼"的基本概念,供您练习和学习.

  • @Disposer移动应用程序和机器人.机器的崛起. (4认同)