Spring boot spring data mongodb中如何实现聚合分页

Spring boot spring data mongodb中如何实现聚合分页,spring-boot,spring-data-mongodb,Spring Boot,Spring Data Mongodb,在使用mongotemplate或mongorepository的spring data mongodb中,如何实现聚合分页您可以使用mongotemplate org.spring.framework.data.mongodb.core.aggregation.aggregation#跳过 及 org.springframework.data.mongodb.core.aggregation.aggregation#limit 聚合agg=newAggregation( 项目(“标签”),

在使用mongotemplate或mongorepository的spring data mongodb中,如何实现聚合分页

您可以使用mongotemplate

org.spring.framework.data.mongodb.core.aggregation.aggregation#跳过
及
org.springframework.data.mongodb.core.aggregation.aggregation#limit
聚合agg=newAggregation(
项目(“标签”),
跳过(10),
限额(10)
);
AggregationResults=mongoTemplate.aggregate(agg,“tags”,TagCount.class);
List tagCount=results.getMappedResults();
除此之外,您还可以使用可分页类来获取结果

公共页面列表(最终可分页){
最终聚合agg=newAggregation(
跳过(pageable.getPageNumber()*pageable.getPageSize()),
限制(pageable.getPageSize())
);
最终列表结果=mongoTemplate
.aggregate(agg、User.class、UserListItemView.class)
.getMappedResults();
返回新的PageImpl(结果,可分页,结果.size())
}

这是对一篇旧文章的回答,但我会提供一个答案,以防其他人在搜索类似内容时出现

在前面的基础上,将results.size()作为PageImpl构造函数中的“total”字段的值,这不会使分页按预期的方式工作。它每次都将总大小设置为页面大小,因此您需要找出查询将返回的实际结果总数:

public Page<UserListItemView> list(final Pageable pageable) {
    long total = getCount(<your property name>, <your property value>);

    final Aggregation agg = newAggregation(
        skip(pageable.getPageNumber() * pageable.getPageSize()),
        limit(pageable.getPageSize())
    );

    final List<UserListItemView> results = mongoTemplate
        .aggregate(agg, User.class, UserListItemView.class)
        .getMappedResults();

    return new PageImpl<>(results, pageable, total);
}
两次运行几乎相同的查询似乎有点低效,但如果要对结果进行分页,则可分页对象必须知道结果的总数,如果您真的希望它的行为类似于分页。如果有人能改进我的方法来获得结果的总数,那将是非常棒的

编辑:这还将提供计数,而且更简单,因为您不需要包装器对象来保存结果,因此您可以使用以下代码块替换以前的整个代码块:

private long getCount(String propertyName, String propertyValue) {
    Query countQuery = new Query(Criteria.where(propertyName).is(propertyValue));
    return mongoTemplate.count(countQuery, Foo.class);
}
根据答案,我为Java编写了代码

使用聚合组获取包含其他分页信息的数据的计数和数组

    AggregationOperation group = Aggregation.group().count().as("total")
            .addToSet(pageable.getPageNumber()).as("pageNumber")
            .addToSet(pageable.getPageSize()).as("pageSize")
            .addToSet(pageable.getOffset()).as("offset")
            .push("$$ROOT").as("data");
    AggregationOperation project = Aggregation.project()
            .andInclude("pageSize", "pageNumber", "total", "offset")
            .and(ArrayOperators.Slice.sliceArrayOf("data").offset((int) pageable.getOffset()).itemCount(pageable.getPageSize()))
            .as("data");
使用聚合项目根据分页信息进行切片

    AggregationOperation group = Aggregation.group().count().as("total")
            .addToSet(pageable.getPageNumber()).as("pageNumber")
            .addToSet(pageable.getPageSize()).as("pageSize")
            .addToSet(pageable.getOffset()).as("offset")
            .push("$$ROOT").as("data");
    AggregationOperation project = Aggregation.project()
            .andInclude("pageSize", "pageNumber", "total", "offset")
            .and(ArrayOperators.Slice.sliceArrayOf("data").offset((int) pageable.getOffset()).itemCount(pageable.getPageSize()))
            .as("data");
使用mongo模板进行聚合

    Aggregation aggr = newAggregation(group, project);
    CustomPage page = mongoTemplate.aggregate(aggregation, Foo.class, CustomPage.class).getUniqueMappedResult();
创建自定义页面

    public class CustomPage {
        private long pageSize;
        private long pageNumber;
        private long offset;
        private long total;
        private List<Foo> data;
    }
公共类自定义页面{
私人长页面大小;
专用长页码;
私人长偏移量;
私人长总;
私人名单数据;
}

要返回具有正确pageable Object值的分页对象,我发现这是最好且简单的方法

Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(Criteria.where("type").is("project")),
                        Aggregation.group("id").last("id").as("id"), Aggregation.project("id"),
                        Aggregation.skip(pageable.getPageNumber() * pageable.getPageSize()),
                        Aggregation.limit(pageable.getPageSize()));


    PageableExecutionUtils.getPage(mongoTemplate.aggregate(aggregation, Draft.class, Draft.class).getMappedResults(), pageable,() -> mongoTemplate.count(Query.of(query).limit(-1).skip(-1), Draft.class));

以下是我的通用解决方案:

公共页面列表(可分页){
//建造你的主要舞台
List mainStages=Arrays.asList(匹配(..),组(..);
返回pageAggregation(可分页、主页面、“目标集合”、ResultObject.class);
}
公共页面聚合(
最终可分页可分页,
最后名单,
最终字符串集合,
最后一节课(课堂){
最终列表阶段WithCount=新的ArrayList(主阶段);
stagesWithCount.add(count().as(“count”));
最终聚合计数AGG=新聚合(stagesWithCount);
最终长计数=可选
.ofNullable(mongoTemplate.aggregate(countAgg、collection、Document.class).getUniqueMappedResult())
.map(doc->((整数)doc.get(“计数”)).longValue()
.orElse(0升);
最终列表阶段WithPaging=new ArrayList(主页面);
stagesWithPaging.add(排序(pageable.getSort());
stagesWithPaging.add(跳过(pageable.getOffset());
stagesWithPaging.add(限制(pageable.getPageSize());
最终聚合结果g=newAggregation(stagesWithPaging);
最终列表结果=mongoTemplate.aggregate(resultAgg、collection、clazz).getMappedResults();
返回新的PageImpl(结果、可分页、计数);
}

另一种方法是扩展
分页和排序存储库
接口。然后,您可以创建如下的
@Aggregation
查询方法:

@Aggregation(pipeline = {
      "{ $match: { someField: ?0 } }",
      "{ $project: { _id: 0, someField: 1} }"
})
List<StuffAggregateModel> aggregateStuff(final String somePropertyName, final Pageable pageable);
聚合(管道={ “{$match:{someField:?0}}”, {$project:{{u id:0,someField:1}} }) List aggregateStuff(最终字符串somePropertyName,最终可分页);
只需从业务逻辑服务类调用它,并构造Pageable(如果需要,它还包含排序选项)并调用repo方法。我喜欢这种方法,因为它非常简单,而且您必须编写的代码量非常小。如果查询(聚合管道)足够简单,那么这可能是最好的解决方案。这种方法的维护编码几乎不费吹灰之力。

应该在mongodb服务器端重复分页,以避免大量数据transfering@Will聚合框架只获取所需的数据。然后我们将数据放入分页类中。在
new PageImpl()
中,将总计设置为
results.size()
在我看来是错误的。您可能需要执行另一个查询才能获得总计结果。@nyxz:您完全正确尝试使用PageableExecutionUtils,而不必另外计算