有效地页面查询结果

时间:2015-03-11 03:27:45

标签: python mongodb mongodb-query pymongo

我有mongo连接到Web应用程序(Python / Pyramid Framework),并且我想在浏览器中显示一个大型集合。由于它非常大,我只想一次渲染100条记录,允许用户点击“加载更多”按钮来检索接下来的100条记录。分页,基本上。

但是,必须按整数分数的降序检索文档,整数分数可以是0-100。有超过100个文档,有多个文档具有相同的分数。因此,对于获得分数小于或等于最后一个得分的下100个文档来说,分页会有点棘手。

如果我可以保存光标,那将是多么棒的,所以当请求下一个100时,光标可以从它停止的位置开始。我正在努力避免只是做query.skip(x).limit(100),因为我读过它不是很有效,因为它基本上会检索你跳过的所有文件。

但是,如果我基于对非索引字段进行排序来检索文档,我不知道跳过/限制方法的效率是多少(如果有的话)。

我知道我可以在这里找到很多其他的选择,并随意提及它们,但我也真的很好奇,如果这样的事情是可能的话。

PS我试过pickle.dumps ......不行。

1 个答案:

答案 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()开销的情况下通过结果“转发仅转发”的有效方式。为了获得最佳性能,请确保您实际上“索引”正在排序的字段。