如何在Spring框架中用Java Aggregation类实现MongoDB年度统计查询
MongoDB聚合查询转Spring Data MongoDB Aggregation类实现
需求:将给定的MongoDB聚合查询转换为Spring Data MongoDB的Aggregation类实现,使用mongoTemplate.aggregate()方法,禁止使用AggregateIterable<Document>(因多数据库连接导致服务器加载缓慢)。
原MongoDB聚合查询
[ { "$addFields": { "__alias_0": { "year": { "$year": "$createdAt" } } } }, { "$addFields": { "__count_by_value": { "k": "status", "v": "$status" } } }, { "$group": { "_id": { "__alias_0": "$__alias_0", "__alias_1": "$__count_by_value.v" }, "__alias_2": { "$sum": { "$cond": [ { "$ne": [ { "$type": "$status" }, "missing" ] }, 1, 0 ] } } } }, { "$project": { "_id": 0, "__alias_0": "$_id.__alias_0", "__alias_1": "$_id.__alias_1", "__alias_2": 1 } }, { "$project": { "Date": "$__alias_0", "Number": "$__alias_2", "status": "$__alias_1", "_id": 0 } }, { "$group": { "_id": { "Date": "$Date" }, "__grouped_docs": { "$push": "$$ROOT" } } }, { "$sort": { "_id.Date.year": 1 } }, { "$unwind": "$__grouped_docs" }, { "$replaceRoot": { "newRoot": "$__grouped_docs" } }, { "$limit": 5000 } ]
示例数据库数据
[ { "year": { "$date": "2021-08-25T08:44:55.576Z" }, "Status": "New" }, { "year": { "$date": "2021-08-25T09:59:49.270Z" }, "Status": "New" }, { "year": { "$date": "2021-08-25T10:27:00.003Z" }, "Status": "Rejected" }, { "year": { "$date": "2022-01-10T08:12:25.816Z" }, "Status": "Rejected" }, { "year": { "$date": "2022-01-10T08:13:07.423Z" }, "Status": "Rejected" } ]
期望查询结果
[ { "year": 2021, "Status": "New", "Amount": 2 }, { "year": 2021, "Status": "Rejected", "Amount": 1 }, { "year": 2022, "Status": "Rejected", "Amount": 2 } ]
Java Aggregation类实现代码
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.*; import java.util.List; // 结果映射类,字段与期望结果对应 class StatusYearStats { private int year; private String Status; private int Amount; // Getters & Setters public int getYear() { return year; } public void setYear(int year) { this.year = year; } public String getStatus() { return Status; } public void setStatus(String status) { Status = status; } public int getAmount() { return Amount; } public void setAmount(int amount) { Amount = amount; } } public class YearlyStatusAggregation { private final MongoTemplate mongoTemplate; public YearlyStatusAggregation(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List<StatusYearStats> getYearlyStatusCount() { // 1. 提取日期字段的年份到__alias_0 // 注:如果实际日期字段是示例中的"year",替换'$createdAt'为'$year' AddFieldsOperation addYearAlias = AddFieldsOperation.addField("__alias_0") .withValue(AggregationExpression.from(MongoExpression.create("{ year: { $year: '$createdAt' } }"))); // 2. 构造状态映射对象__count_by_value AddFieldsOperation addCountByValue = AddFieldsOperation.addField("__count_by_value") .withValue(AggregationExpression.from(MongoExpression.create("{ k: 'status', v: '$status' }"))); // 3. 按年份+状态分组统计数量(排除status字段缺失的文档) GroupOperation groupByYearStatus = Aggregation.group("__alias_0", "__count_by_value.v") .sum(AggregationExpression.from(MongoExpression.create( "{ $cond: [ { $ne: [ { $type: '$status' }, 'missing' ] }, 1, 0 ] }" ))).as("__alias_2"); // 4. 第一次投影:重组分组后的字段 ProjectionOperation project1 = Aggregation.project() .and("_id.__alias_0").as("__alias_0") .and("_id.__alias_1").as("__alias_1") .and("__alias_2").as("__alias_2") .andExclude("_id"); // 5. 第二次投影:重命名为目标字段名 ProjectionOperation project2 = Aggregation.project() .and("__alias_0").as("Date") .and("__alias_2").as("Number") .and("__alias_1").as("status") .andExclude("_id"); // 6. 按年份分组,聚合同一年的所有状态统计文档 GroupOperation groupByDate = Aggregation.group("Date") .push("$$ROOT").as("__grouped_docs"); // 7. 按年份升序排序 SortOperation sortByYear = Aggregation.sort(Sort.Direction.ASC, "_id.Date.year"); // 8. 展开分组后的文档数组 UnwindOperation unwindDocs = Aggregation.unwind("__grouped_docs"); // 9. 将展开后的文档设为根文档 ReplaceRootOperation replaceRoot = Aggregation.replaceRoot("__grouped_docs"); // 10. 限制返回结果数量 LimitOperation limit = Aggregation.limit(5000); // 组装聚合管道 Aggregation aggregation = Aggregation.newAggregation( addYearAlias, addCountByValue, groupByYearStatus, project1, project2, groupByDate, sortByYear, unwindDocs, replaceRoot, limit ); // 执行聚合并映射到结果类 return mongoTemplate.aggregate(aggregation, "your_collection_name", StatusYearStats.class).getMappedResults(); } }
注意事项
- 替换代码中的
your_collection_name为实际MongoDB集合名称。 - 若日期字段是示例数据中的
year而非原查询的createdAt,需修改第一个AddFieldsOperation中的'$createdAt'为'$year'。 - 结果类
StatusYearStats的字段名需与期望结果严格对应(注意大小写)。
内容的提问来源于stack exchange,提问作者YourDaddyProgrammer
相关产品推荐
相关产品推荐

