如何在Spring Boot中实现MongoDB Aggregation的Facet操作
问题:Spring Boot中无法复现Mongo Compass的聚合查询结果
我能在Mongo Compass中执行以下聚合查询得到正确结果,但在Spring Boot应用里始终无法复现相同效果。
Compass中的聚合查询:
[ { $match: { $and: [ { channelId: { $in: [1001], }, }, ], }, }, { $facet: { operationTypes: [ { $group: { _id: "$operationType", count: { $sum: 1 }, }, }, { $project: { _id: 0, operationType: "$_id", count: 1, }, }, ], states: [ { $group: { _id: "$state", count: { $sum: 1 }, }, }, { $project: { _id: 0, state: "$_id", count: 1, }, }, ], results: [ { $group: { _id: "$result", count: { $sum: 1 }, }, }, { $project: { _id: 0, result: "$_id", count: 1, }, }, ], }, }, ]
尝试过的无效方案
方案1:自定义Aggregation调用方法
我尝试创建AggregationOperation列表并调用以下方法,但没有效果:
public List<AggregationResultTest> getAggregation(Aggregation aggregation) { AggregationResults<AggregationResultTest> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, AggregationResultTest.class); return results.getMappedResults(); }
方案2:Compass导出的代码
我也尝试使用Mongo Compass的EXPORT TO LANGUAGE功能生成代码,但同样无效,生成的代码如下:
import java.util.Arrays; import org.bson.Document; Arrays.asList(new Document("$match", new Document("channelId", 1001L)), new Document("$facet", new Document("operationTypes", Arrays.asList(new Document("$group", new Document("_id", "$operationType") .append("count", new Document("$sum", 1L))), new Document("$project", new Document("_id", 0L) .append("operationType", "$_id") .append("count", 1L)))) .append("states", Arrays.asList(new Document("$group", new Document("_id", "$state") .append("count", new Document("$sum", 1L))), new Document("$project", new Document("_id", 0L) .append("state", "$_id") .append("count", 1L)))) .append("results", Arrays.asList(new Document("$group", new Document("_id", "$result") .append("count", new Document("$sum", 1L))), new Document("$project", new Document("_id", 0L) .append("result", "$_id") .append("count", 1L))))))
需求
需要在如下结构的Repository中实现该聚合逻辑:
@Repository public class OperationHistoryRepository { private final MongoTemplate mongoTemplate; // 聚合方法需要写在这里 }
正确实现方案
1. 定义结果映射实体类
首先创建对应$facet输出结果的实体类:
// 单个统计项的实体 public class CountItem { private String operationType; private String state; private String result; private Integer count; // 生成getter、setter和无参构造方法 } // 聚合结果的根实体 public class AggregationResult { private List<CountItem> operationTypes; private List<CountItem> states; private List<CountItem> results; // 生成getter、setter和无参构造方法 }
2. 在Repository中实现聚合方法
@Repository public class OperationHistoryRepository { private static final String COLLECTION_NAME = "your_collection_name"; // 替换为实际集合名 private final MongoTemplate mongoTemplate; // 构造方法注入MongoTemplate public OperationHistoryRepository(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public AggregationResult getAggregationResult(Long channelId) { // 1. 构建$match阶段 MatchOperation matchOperation = Aggregation.match(Criteria.where("channelId").in(channelId)); // 2. 构建operationTypes的子聚合 Aggregation operationTypeAgg = Aggregation.newAggregation( Aggregation.group("operationType").count().as("count"), Aggregation.project("count").and("_id").as("operationType").andExclude("_id") ); // 3. 构建states的子聚合 Aggregation stateAgg = Aggregation.newAggregation( Aggregation.group("state").count().as("count"), Aggregation.project("count").and("_id").as("state").andExclude("_id") ); // 4. 构建results的子聚合 Aggregation resultAgg = Aggregation.newAggregation( Aggregation.group("result").count().as("count"), Aggregation.project("count").and("_id").as("result").andExclude("_id") ); // 5. 构建$facet阶段 FacetOperation facetOperation = Aggregation.facet(operationTypeAgg, stateAgg, resultAgg) .as("operationTypes", "states", "results"); // 6. 组装完整聚合 Aggregation aggregation = Aggregation.newAggregation(matchOperation, facetOperation); // 7. 执行聚合并映射结果 AggregationResults<AggregationResult> results = mongoTemplate.aggregate( aggregation, COLLECTION_NAME, AggregationResult.class ); return results.getUniqueMappedResult(); } }
说明
- 使用Spring Data MongoDB提供的
Aggregation工具类构建聚合阶段,比直接用Document更易维护且类型安全 - 因为$facet返回的是单个包含三个列表的文档,所以用
getUniqueMappedResult()获取结果,而不是getMappedResults() - 确保实体类的字段名和聚合输出的键名完全一致,Spring Data会自动映射
内容的提问来源于stack exchange,提问作者Serdar
相关产品推荐
相关产品推荐

