Long totalElements = mongockTemplate.count(new Query(),"product");
int page =0;
Long pageSize = 20L;
String lastId = "5f71a7fe1b961449094a30aa"; //this is the last if of the precedent page
for(int i=0; i<(totalElements/pageSize); i++){
page +=1;
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("_id").gt(new ObjectId(lastId))),
Aggregation.sort(Sort.Direction.ASC,"_id"),
new CustomAggregationOperation(queryOffersByProduct),
Aggregation.limit((long)pageSize)
);
List<ProductGroupedOfferDTO> productGroupedOfferDTOS = mongockTemplate.aggregate(aggregation,"product",ProductGroupedOfferDTO.class).getMappedResults();
lastId = productGroupedOfferDTOS.get(productGroupedOfferDTOS.size()-1).getId();
9条答案
按热度按时间eanckbw91#
解决这个问题的一个方法是,如果您有大量文档,并且您要以 sorted 顺序显示它们(如果您不这样做,我不确定
skip
是否有用),则使用排序所依据的键来选择下一页结果。如果你从
然后将游标返回的 last 文档的创建日期提取到变量
max_created_date_from_last_result
中,则可以使用效率高得多的查询(假设在created_date
上有一个索引)获取下一页vc6uscn92#
来自MongoDB documentation:
寻呼成本
遗憾的是,skip可能(非常)昂贵,并且要求服务器从集合或索引的开始处遍历到偏移/skip位置,然后才能开始返回数据页(限制)。随着页数的增加,skip将变得更慢,CPU密集度更高,并且可能在较大的集合中受到IO限制。
基于范围的分页可以更好地使用索引,但不允许您轻松地跳转到特定页。
你得问自己一个问题:多久需要40000页?也可以参考this文章;
dzjeubhm3#
我发现把这两个概念结合在一起(skip+limit和find+limit)性能很好。skip+limit的问题是当你有很多文档(特别是大文档)时性能很差。find+limit的问题是你不能跳转到任意页面。我希望能够不按顺序分页。
我采取的步骤是:
1.根据您希望如何对文档进行排序来创建索引,或者只使用default _id索引(这是我使用的)
1.知道起始值、页面大小和要跳转到的页面
1.查找+限制页面结果
如果我想得到第5432页的16条记录(用javascript编写),大致如下所示:
这是因为即使跳过数百万条记录,跳过投影索引的速度也非常快(这就是我正在做的)如果您运行
explain("executionStats")
,它仍然有一个很大的totalDocsExamined
数,但由于在索引上的投影,它非常快(本质上,数据blob永远不会被检查)。然后,有了页面开始的值在手,就可以非常快地获取下一页。yyyllmsg4#
我把两个答案连在一起。
问题是当你使用跳过和限制,没有排序,它只是分页的顺序表在相同的顺序,因为你写数据到表,所以引擎需要作出第一个临时索引。是更好地使用ready _id索引:)你需要使用排序by _id。
在PHP中它将是
k3fezbri5#
我将建议一个更激进的方法。合并跳过/限制(实际上是一种边缘情况),使用基于范围的存储桶进行排序,页面不是基于固定数量的文档,而是基于一个时间范围(或者你的排序)。所以你有每个时间范围的顶级页面,如果你需要跳过/限制,你有那个时间范围内的子页面,但是我怀疑可以把bucket做得足够小,根本不需要跳过/限制。通过使用排序索引,这可以避免光标遍历整个库存到达最后一页。
xxls0lw86#
我的收藏中有大约130万个文档(不是那么大),正确地索引,但仍然受到这个问题的严重影响。
阅读其他答案后,向前的解决方案是明确的;分页的集合必须通过类似于SQL的自动递增值的计数整数而不是基于时间的值来排序。
问题出在
skip
上;没有其他办法可以绕过它;如果您使用skip
,那么当您的集合增长时,您一定会遇到这个问题。使用带索引的计数整数允许使用索引跳转而不是跳过。这不适用于基于时间的值,因为您无法计算基于时间的跳转位置,因此跳过是后一种情况下的唯一选项。
另一方面,
通过为每个文档分配计数,写入性能将受到影响;因为所有文档都必须按顺序插入。这对我的用例来说没问题,但我知道这个解决方案并不适合所有人。
得票最多的答案似乎不适用于我的情况,但这个答案适用(我需要能够通过任意页码向前查找,而不是一次一页)。
另外,如果你正在处理
delete
,这也很难,但仍然有可能,因为MongoDB支持$inc
与一个负值批量更新.幸运的是,我不必处理删除在我维护的应用程序.把这个写下来,作为对未来的自己的一个提醒。用我现在正在处理的应用程序解决这个问题可能太麻烦了,但是下一次,如果我遇到类似的情况,我会构建一个更好的应用程序。
3hvapo4f7#
如果你有mongos的默认id是ObjectId,那就用它来代替,这可能是大多数项目最可行的选择。
如the official mongo docs所述:
skip()方法要求服务器在开始返回结果之前从输入结果集的开头开始扫描。随着偏移量的增加,skip()将变慢。
范围查询可以使用索引来避免扫描不需要的文档,与使用skip()进行分页相比,随着偏移量的增加,范围查询通常会产生更好的性能。
降序(示例):
这里是升序示例。
qlckcl4x8#
如果知道要限制的元素的ID。
这是一个很棒的解决方案,很有魅力
c2e8gylq9#
为了更快地分页,不要使用skip()函数,而要使用limit()和find()来查询前一页的最后一个id。
这里是一个例子,我正在查询超过吨的文档使用spring Boot :
}