对集合进行排序和分页
Sort and paginate a collection
如何对按非唯一字段排序的查询进行分页?例如,集合中的文档可能是(按 s:1 排序,然后是 _id:-1):
{_id: 19, s: 3},
{_id: 17, s: 3},
{_id: 58, s: 4},
// etc...
有一个简单的 limit/skip 方法可以工作......慢慢来。
是否可以使用类似的东西:
db.collection.find()
.sort({s:1, _id:-1})
.min({s:3, _id:17}) // this does not work as wanted!
.limit(2);
检索
{_id: 17, s: 3},
{_id: 58, s: 4}
?
db.t1.drop()
db.t1.save({_id:19, s:3})
db.t1.save({_id:17, s:3})
db.t1.save({_id:58, s:4})
db.t1.find().sort({s:1, _id:-1}).skip(1).limit(2)
--Result
{ "_id" : 17, "s" : 3 }
{ "_id" : 58, "s" : 4 }
-$
如果你想按 "page numbers" 分页,那么你几乎只能使用 .limit()
and .skip()
方法对你的键进行排序后应用。您可能已经阅读了一些内容,发现它是 "not very efficient",主要是由于 "skipping" 通过 "n" 结果到达某个页面的成本。
但原则在您需要的地方是合理的:
db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>)
如果您只需要在分页中移动 "forward",则可以使用更快的替代方法,并且对于 "sorted" 结果也是如此。
关键是保持对 "s" 的 "last seen" 值的引用,然后通常是 _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 },
为了获得 "two" 个结果中的 "first page" 个,您的第一个查询很简单:
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"的选择需要从一个值"less than or equal to"开始(因为排序的方向)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
})
因此,通过遵循该逻辑模式,您可以 "store" 从 "previousc page" 结果中找到的信息,并非常有效地 "forward" 移动结果。
但是如果你需要跳转到"page 20"或类似类型的操作,那么你就会被.limit()
和.skip()
卡住。那样比较慢,但这取决于你能忍受什么。
如何对按非唯一字段排序的查询进行分页?例如,集合中的文档可能是(按 s:1 排序,然后是 _id:-1):
{_id: 19, s: 3},
{_id: 17, s: 3},
{_id: 58, s: 4},
// etc...
有一个简单的 limit/skip 方法可以工作......慢慢来。
是否可以使用类似的东西:
db.collection.find()
.sort({s:1, _id:-1})
.min({s:3, _id:17}) // this does not work as wanted!
.limit(2);
检索
{_id: 17, s: 3},
{_id: 58, s: 4}
?
db.t1.drop()
db.t1.save({_id:19, s:3})
db.t1.save({_id:17, s:3})
db.t1.save({_id:58, s:4})
db.t1.find().sort({s:1, _id:-1}).skip(1).limit(2)
--Result
{ "_id" : 17, "s" : 3 }
{ "_id" : 58, "s" : 4 }
-$
如果你想按 "page numbers" 分页,那么你几乎只能使用 .limit()
and .skip()
方法对你的键进行排序后应用。您可能已经阅读了一些内容,发现它是 "not very efficient",主要是由于 "skipping" 通过 "n" 结果到达某个页面的成本。
但原则在您需要的地方是合理的:
db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>)
如果您只需要在分页中移动 "forward",则可以使用更快的替代方法,并且对于 "sorted" 结果也是如此。
关键是保持对 "s" 的 "last seen" 值的引用,然后通常是 _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 },
为了获得 "two" 个结果中的 "first page" 个,您的第一个查询很简单:
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"的选择需要从一个值"less than or equal to"开始(因为排序的方向)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
})
因此,通过遵循该逻辑模式,您可以 "store" 从 "previousc page" 结果中找到的信息,并非常有效地 "forward" 移动结果。
但是如果你需要跳转到"page 20"或类似类型的操作,那么你就会被.limit()
和.skip()
卡住。那样比较慢,但这取决于你能忍受什么。