spring 数据 mongodb 聚合管道的分页结果
Paging results of aggregation pipeline with spring data mongodb
我在对聚合管道的结果进行分页时遇到了一些麻烦。在查看 之后,我想出了一个感觉像是 hacky 的解决方案。我先进行匹配查询,然后按我搜索的字段分组,统计结果,将值映射到一个私有的class:
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, Athlete.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;
}
}
通过这种方式,我能够为要返回的页面对象提供 "total" 值:
public Page<Athlete> findAllByName(String name, Pageable pageable) {
long total = getCount("team.name", name);
Aggregation aggregation = getAggregation("team.name", name, pageable);
List<Athlete> aggregationResults = mongoTemplate.aggregate(
aggregation, Athlete.class, Athlete.class
).getMappedResults();
return new PageImpl<>(aggregationResults, pageable, total);
}
您可以看到得到结果总数的聚合与我要执行的实际聚合没有太大区别:
MatchOperation matchOperation = match(Criteria.where(propertyName).is(propertyValue));
SkipOperation skipOperation = skip((long) (pageable.getPageNumber() * pageable.getPageSize()));
LimitOperation limitOperation = limit(pageable.getPageSize());
SortOperation sortOperation = sort(pageable.getSort());
return newAggregation(matchOperation, skipOperation, limitOperation, sortOperation);
这确实有效,但是,正如我所说的,感觉很老套。有没有一种方法可以获取 PageImpl 实例的计数而不必 运行 查询两次?
你的问题帮助我解决了使用聚合进行分页的相同问题,因此我进行了一些挖掘并找到了解决你问题的方法。我知道现在有点晚了,但有人可能会用到这个答案。我绝不是 Mongo 专家,所以如果我所做的是错误的做法或表现不佳,请随时告诉我。
使用组,我们可以将根文档添加到一个集合中并进行计数。
group().addToSet(Aggregation.ROOT).as("documents")
.count().as("count"))
这是我针对与您遇到的几乎完全相同的问题的解决方案。
private Page<Customer> searchWithFilter(final String filterString, final Pageable pageable, final Sort sort) {
final CustomerAggregationResult aggregationResult = new CustomerAggregationExecutor()
.withAggregations(match(new Criteria()
.orOperator(
where("firstName").regex(filterString),
where("lastName").regex(filterString))),
skip((long) (pageable.getPageNumber() * pageable.getPageSize())),
limit(pageable.getPageSize()),
sort(sort),
group()
.addToSet(Aggregation.ROOT).as("documents")
.count().as("count"))
.executeAndGetResult(operations);
return new PageImpl<>(aggregationResult.getDocuments(), pageable, aggregationResult.getCount());
}
CustomerAggregationResult.java
@Data
public class CustomerAggregationResult {
private int count;
private List<Customer> documents;
public static class PageableAggregationExecutor {
private Aggregation aggregation;
public CustomerAggregationExecutor withAggregations(final AggregationOperation... operations) {
this.aggregation = newAggregation(operations);
return this;
}
@SuppressWarnings("unchecked")
public CustomerAggregationResult executeAndGetResult(final MongoOperations operations) {
return operations.aggregate(aggregation, Customer.class, CustomerAggregationResult.class)
.getUniqueMappedResult();
}
}
}
真心希望能有所帮助。
编辑:我最初创建了一个带有 List 的通用 PageableAggregationResult 但是这个 returns 一个 IllegalArgumentException 因为我传递 PageableAggregationResult.class 没有 T 的类型。如果我找到解决方案,我会编辑这个回答,因为我希望最终能够聚合多个集合。
我在对聚合管道的结果进行分页时遇到了一些麻烦。在查看
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, Athlete.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;
}
}
通过这种方式,我能够为要返回的页面对象提供 "total" 值:
public Page<Athlete> findAllByName(String name, Pageable pageable) {
long total = getCount("team.name", name);
Aggregation aggregation = getAggregation("team.name", name, pageable);
List<Athlete> aggregationResults = mongoTemplate.aggregate(
aggregation, Athlete.class, Athlete.class
).getMappedResults();
return new PageImpl<>(aggregationResults, pageable, total);
}
您可以看到得到结果总数的聚合与我要执行的实际聚合没有太大区别:
MatchOperation matchOperation = match(Criteria.where(propertyName).is(propertyValue));
SkipOperation skipOperation = skip((long) (pageable.getPageNumber() * pageable.getPageSize()));
LimitOperation limitOperation = limit(pageable.getPageSize());
SortOperation sortOperation = sort(pageable.getSort());
return newAggregation(matchOperation, skipOperation, limitOperation, sortOperation);
这确实有效,但是,正如我所说的,感觉很老套。有没有一种方法可以获取 PageImpl 实例的计数而不必 运行 查询两次?
你的问题帮助我解决了使用聚合进行分页的相同问题,因此我进行了一些挖掘并找到了解决你问题的方法。我知道现在有点晚了,但有人可能会用到这个答案。我绝不是 Mongo 专家,所以如果我所做的是错误的做法或表现不佳,请随时告诉我。
使用组,我们可以将根文档添加到一个集合中并进行计数。
group().addToSet(Aggregation.ROOT).as("documents")
.count().as("count"))
这是我针对与您遇到的几乎完全相同的问题的解决方案。
private Page<Customer> searchWithFilter(final String filterString, final Pageable pageable, final Sort sort) {
final CustomerAggregationResult aggregationResult = new CustomerAggregationExecutor()
.withAggregations(match(new Criteria()
.orOperator(
where("firstName").regex(filterString),
where("lastName").regex(filterString))),
skip((long) (pageable.getPageNumber() * pageable.getPageSize())),
limit(pageable.getPageSize()),
sort(sort),
group()
.addToSet(Aggregation.ROOT).as("documents")
.count().as("count"))
.executeAndGetResult(operations);
return new PageImpl<>(aggregationResult.getDocuments(), pageable, aggregationResult.getCount());
}
CustomerAggregationResult.java
@Data
public class CustomerAggregationResult {
private int count;
private List<Customer> documents;
public static class PageableAggregationExecutor {
private Aggregation aggregation;
public CustomerAggregationExecutor withAggregations(final AggregationOperation... operations) {
this.aggregation = newAggregation(operations);
return this;
}
@SuppressWarnings("unchecked")
public CustomerAggregationResult executeAndGetResult(final MongoOperations operations) {
return operations.aggregate(aggregation, Customer.class, CustomerAggregationResult.class)
.getUniqueMappedResult();
}
}
}
真心希望能有所帮助。
编辑:我最初创建了一个带有 List 的通用 PageableAggregationResult 但是这个 returns 一个 IllegalArgumentException 因为我传递 PageableAggregationResult.class 没有 T 的类型。如果我找到解决方案,我会编辑这个回答,因为我希望最终能够聚合多个集合。