我有mongo连接到Web应用程序(Python / Pyramid Framework),并且我想在浏览器中显示一个大型集合。由于它非常大,我只想一次渲染100条记录,允许用户点击“加载更多”按钮来检索接下来的100条记录。分页,基本上。
但是,必须按整数分数的降序检索文档,整数分数可以是0-100。有超过100个文档,有多个文档具有相同的分数。因此,对于获得分数小于或等于最后一个得分的下100个文档来说,分页会有点棘手。
如果我可以保存光标,那将是多么棒的,所以当请求下一个100时,光标可以从它停止的位置开始。我正在努力避免只是做query.skip(x).limit(100)
,因为我读过它不是很有效,因为它基本上会检索你跳过的所有文件。
但是,如果我基于对非索引字段进行排序来检索文档,我不知道跳过/限制方法的效率是多少(如果有的话)。
我知道我可以在这里找到很多其他的选择,并随意提及它们,但我也真的很好奇,如果这样的事情是可能的话。
PS我试过pickle.dumps
......不行。
答案 0 :(得分:3)
在这里原谅JavaScript,但它确实可以作为一个例子,可以在shell中为每个人复制,我很快就会这样做。任何语言的要点基本相同。
考虑以下文件:
{ "_id": 1, "score": 2 }
{ "_id": 2, "score": 2 }
{ "_id": 3, "score": 5 }
{ "_id": 4, "score": 4 }
{ "_id": 5, "score": 5 }
{ "_id": 6, "score": 3 }
{ "_id": 7, "score": 1 }
{ "_id": 8, "score": 2 }
现在,对于此处的示例,结果将按“得分”降序排序,并且一次限制为2个结果的“页面”。然后将按如下方式发出初始查询:
var seenIds = [];
var lastScore = 0;
var cursor = db.sorted.find({}).sort({ "score": -1 }).limit(2);
cursor.forEach(function(doc) {
printjson(doc);
if (doc.score != lastScore)
seenIds = [];
seenIds.push(doc._id)
lastScore = doc.score;
});
输出将来自排序结果:
{ "_id" : 3, "score" : 5 }
{ "_id" : 5, "score" : 5 }
因此,基本的想法是,您可以通过光标迭代执行您需要的任何处理,例如输出流或构建另一个变量的内容。在迭代时,您希望存储在结果的最后一页中看到的“唯一”_id
值的数组。您还保留存在的已排序字段的值。为了达到最佳效果,您只需要保留尽可能多的“唯一”_id
值,就像上次“看到”得分的当前值一样。
当然,这些变量需要存储在会话存储等请求之间。这样做就可以在下次请求时获取它们。
在随后的“加载更多”请求中,您将发出更改后的查询,如下所示:
var cursor = db.sorted.find({
"_id": { "$nin": seenIds }, "score": { "$lte": lastScore }
}).sort({ "score": -1 }).limit(2);
cursor.forEach(function(doc) {
printjson(doc);
if (doc.score != lastScore)
seenIds = [];
seenIds.push(doc._id)
lastScore = doc.score;
});
注意到存储中这些变量的输入状态如下所示:
seenIds = [ 3, 5 ];
lastScore = 5;
所以查询的输出是:
{ "_id" : 4, "score" : 4 }
{ "_id" : 6, "score" : 3 }
现在会话存储中的那些新状态变量将包含不同的值:
seenIds = [ 6 ];
lastScore = 3;
获取“加载更多”请求使用这些请求:
{ "_id" : 1, "score" : 2 }
{ "_id" : 2, "score" : 2 }
因此,当你看到这个问题的核心是按“得分”这样的东西进行排序时,一般的想法是只检索那些“小于或等于”(按降序排列)到“lastSeen”的项目在页面中检索到的值。
当然,很多项目可能具有相同的“得分”值,因此为了对抗这一点,您需要保留已经为相同匹配得分值看到的“唯一ID”列表。< / p>
查询选择基本上是这样说的,“得到的所有结果的得分都小于或等于我上次看到的值,但排除了我从结果中看到的任何内容。
这样做可以在没有.skip()
和.limit()
开销的情况下通过结果“转发仅转发”的有效方式。为了获得最佳性能,请确保您实际上“索引”正在排序的字段。