在spring数据中mongodb如何实现聚合的分页

时间:2015-12-23 01:34:34

标签: spring-boot spring-data-mongodb

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

7 个答案:

答案 0 :(得分:6)

除了ssouris solution,您还可以使用 Pageable 类来获得结果。

public Page<UserListItemView> list(final Pageable pageable) {

    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, results.size())
}

答案 1 :(得分:5)

您可以使用MongoTemplate

org.spring.framework.data.mongodb.core.aggregation.Aggregation#skip
        and 
org.springframework.data.mongodb.core.aggregation.Aggregation#limit

Aggregation agg = newAggregation(
        project("tags"),
        skip(10),
        limit(10)
);

AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

答案 2 :(得分:5)

这是旧帖子的答案,但我会提供答案,以防其他人在搜索此类内容时出现。

以前一个solution by Fırat KÜÇÜK为基础,将results.size()作为&#34; total&#34;的值。 PageImpl构造函数中的字段不会使分页工作正常,您希望分页工作。它会将总大小设置为每次的页面大小,因此您需要找出查询返回的实际结果总数:

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);
}

现在,获得结果总数的最佳方法是另一个问题,这是我目前正在试图解决的问题。我尝试过的方法(并且它起作用)是几乎运行相同的聚合两次,(一次获得总计数,再次获得分页的实际结果)但仅使用MatchOperation后跟GroupOperation来获取计数:

private long getCount(String propertyName, String propertyValue) {
    MatchOperation matchOperation = match(Criteria.where(propertyName).is(propertyValue));
    GroupOperation groupOperation = group(propertyName).count().as("count");
    Aggregation aggregation = newAggregation(matchOperation, groupOperation);
    return mongoTemplate.aggregate(aggregation, Foo.class, NumberOfResults.class).getMappedResults().get(0).getCount();
}

private class NumberOfResults {
    private int count;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

两次运行几乎相同的查询似乎效率低下,但是如果要打印结果,可分页对象必须知道结果的总数,如果您真的希望它的行为像分页。如果有人可以改进我的方法来获得结果总数,那就太棒了!

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

private long getCount(String propertyName, String propertyValue) {
    Query countQuery = new Query(Criteria.where(propertyName).is(propertyValue));
    return mongoTemplate.count(countQuery, Foo.class);
}

答案 3 :(得分:1)

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

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));

答案 4 :(得分:0)

根据答案https://stackoverflow.com/a/39784851/4546949,我编写了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");

使用Aggregation项目根据分页信息进行切片。

    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();

创建一个CustomPage。

    public class CustomPage {
        private long pageSize;
        private long pageNumber;
        private long offset;
        private long total;
        private List<Foo> data;
    }

答案 5 :(得分:0)

这是我的通用解决方案:

public Page<ResultObject> list(Pageable pageable) {
    // build your main stages
    List<AggregationOperation> mainStages = Arrays.asList(match(....), group(....));
    return pageAggregation(pageable, mainStages, "target-collection", ResultObject.class);
}

public <T> Page<T> pageAggregation(
        final Pageable pageable,
        final List<AggregationOperation> mainStages,
        final String collection,
        final Class<T> clazz) {
    final List<AggregationOperation> stagesWithCount = new ArrayList<>(mainStages);
    stagesWithCount.add(count().as("count"));
    final Aggregation countAgg = newAggregation(stagesWithCount);
    final Long count = Optional
            .ofNullable(mongoTemplate.aggregate(countAgg, collection, Document.class).getUniqueMappedResult())
            .map(doc -> ((Integer) doc.get("count")).longValue())
            .orElse(0L);

    final List<AggregationOperation> stagesWithPaging = new ArrayList<>(mainStages);
    stagesWithPaging.add(sort(pageable.getSort()));
    stagesWithPaging.add(skip(pageable.getOffset()));
    stagesWithPaging.add(limit(pageable.getPageSize()));
    final Aggregation resultAgg = newAggregation(stagesWithPaging);
    final List<T> result = mongoTemplate.aggregate(resultAgg, collection, clazz).getMappedResults();

    return new PageImpl<>(result, pageable, count);
}

答案 6 :(得分:0)

另一种方法是扩展PagingAndSortingRepository<T, ID>接口。然后,您可以创建一个@Aggregation查询方法,如下所示:

@Aggregation(pipeline = {
      "{ $match: { someField: ?0 } }",
      "{ $project: { _id: 0, someField: 1} }"
})
List<StuffAggregateModel> aggregateStuff(final String somePropertyName, final Pageable pageable);

只需从您的业务逻辑服务类中调用此方法,然后构造Pageable(如果需要,它还包含排序选项),然后调用repo方法。我之所以喜欢这种方法,是因为您必须编写的代码简单且完全减少。如果您的查询(聚合管道)足够简单,那么这可能是最佳解决方案。这种方法的维护编码几乎毫不费力。