MongoDB聚合的高效分页



为了提高效率,Mongo文档建议限制语句紧跟在排序语句之后,从而得到一些荒谬的结果:

collection.find(f).sort(s).limit(l).skip(p)

我说这有点荒谬,因为它似乎是说取前l个项目,然后去掉这些l的第一个p。由于p通常大于l,你会认为你最终会没有结果,但在实践中,你最终会得到l个结果。

聚合效果如您所料:

collection.aggregate({$unwind: u}, {$group: g},{$match: f}, {$sort: s}, {$limit: l}, {$skip: p})

如果p>=l,则返回0个结果。

collection.aggregate({$unwind: u}, {$group: g}, {$match: f}, {$sort: s}, {$skip: p}, {$limit: l})

有效,但文档似乎暗示,如果匹配返回的结果集大于工作内存,那么这将失败。这是真的吗?如果是这样,是否有更好的方法对通过聚合返回的结果集执行分页?

来源:本页末尾的"在版本2.4中更改"评论:http://docs.mongodb.org/manual/reference/operator/aggregation/sort/

在MongoDB中,像limitsortskip这样的游标方法(即使用find()时)可以按任何顺序应用=>顺序无关紧要。find()返回应用了修改的光标。排序总是在限制之前完成,跳过也在限制之前进行。换句话说,顺序是:排序->跳过->限制

聚合框架不返回DB游标。相反,它返回一个带有聚合结果的文档。它的工作原理是在管道的每一步产生中间结果,因此操作顺序非常重要。

我想MongoDB不支持order for cursor修饰符方法,因为它是在内部实现的。

您不能对聚合框架的结果进行分页,因为只有一个文档具有结果。您仍然可以使用skip和limit对常规查询进行分页,但更好的做法是使用范围查询,因为它使用索引的效率很高。

更新:

由于v2.6 Mongo聚合框架返回一个游标,而不是单个文档。比较v2.4和v2.6。

文档似乎暗示,如果匹配返回的结果集大于工作内存,则此(聚合)将失败。这是真的吗?

否。例如,您甚至可以在不使用$match运算符的情况下对大于物理内存的集合进行聚合。这可能很慢,但应该有效。如果$match返回的内容大于RAM,则没有问题。

以下是实际的管道限制。

http://docs.mongodb.org/manual/core/aggregation-pipeline-limits/

$match运算符不会单独导致内存问题。如文件中所述,$group$sort是常见的反派。它们是累积的,可能需要访问整个输入集才能产生任何输出。如果它们将过多的数据加载到物理内存中,就会失败。

如果是,是否有更好的方法对通过聚合返回的结果集执行分页?

我已经正确地说过,不能对聚合结果进行"分页"(应用$skip$limit),因为它只是一个MongoDB文档。但是您可以对聚合管道的中间结果进行"分页"。

在管道上使用$limit将有助于将结果集保持在16MB的范围内,即BSON文档的最大大小。即使收藏品增加了,你也应该是安全的。

$group,特别是$sort可能会出现问题。如果它们确实发生了,您可以创建"排序友好"的索引来处理它们。看看关于索引策略的文档。

http://docs.mongodb.org/manual/tutorial/sort-results-with-indexes/

最后,请注意$skip对性能没有帮助。相反,它们往往会减慢应用程序的速度,因为它迫使MongoDB扫描每一个跳过的文档,以达到集合中所需的点。

http://docs.mongodb.org/manual/reference/method/cursor.skip/

MongoDB建议在$limit之前$sort是绝对正确的,因为当它发生时,它优化了为前n个结果执行操作所需的内存。

只是您提出的解决方案不适合您的用例,即分页。

您可以将查询修改为,以从该优化中获益。

collection.aggregate([
{
$unwind: u
}, 
{
$group: g
},
{
$match: f
}, 
{
$sort: s
}, 
{
$limit: l+p
},
{ 
$skip: p
}
]);

或对于find查询

collection.find(f).sort(s).limit(l+p).skip(p)

不过,正如您所看到的,使用大分页,即使进行了此优化,内存也会越来越多

最新更新