在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

你正在谈论的概念可以被称为“前向寻呼”。 这样做的一个好的原因是不同于使用.skip().limit()修饰符,这不能用于“返回”到前一页,或者确实“跳过”到特定页面。 至less不用大量的努力来存储“看到”或“发现”的页面,所以如果这种types的“链接到页面”分页是你想要的,那么你最好坚持使用.skip().limit()方法,尽pipeperformance有缺点。

如果这是一个可行的select,只有“前进”,那么这里是基本的概念:

 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 } 

要将其分成按页面sorting的两个页面,那么您基本上需要知道的是“已经看到”的内容并排除这些结果。 所以看第一页:

 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 } 

你实际上持有多less“显示”取决于你的结果是如何“细微”的,这个价值可能会发生改变。 在这种情况下,您可以检查当前的“排名”分数是否等于lastSeen值,并放弃当前的seenIds内容,因此它不会增长太多。

这是“向前传呼”的基本概念,供您练习和学习。