Spring MongoDB Aggregation 按条件分组数据的实现求助
Spring MongoDB Aggregation 实现条件分组操作
需求说明
现有MongoDB文档数据如下:
[ { "name": "AAA", "age": 14, "country": "us" }, { "name": "BBB", "age": 13, "country": "us" }, { "name": "CCC", "age": 12, "country": null }, { "name": "DDD", "age": 12, "country": null } ]
分组规则:
- 当
country不为null时,按country字段分组 - 当
country为null时,按age字段分组
期望输出结果格式:
[ { "country": "us", "items": [ { "name": "AAA", "age": 14 }, { "name": "BBB", "age": 13 } ] }, { "age": 12, "country": null, "items": [ { "name": "CCC" }, { "name": "DDD" } ] } ]
实现方案
通过MongoDB聚合管道+Spring Data MongoDB的AggregationAPI实现,核心思路是先生成动态分组键,再按该键分组,最后调整输出格式。
1. 定义实体与结果DTO
// 原始数据实体类 class User { private String name; private Integer age; private String country; // getter、setter、构造方法省略 } // 分组结果接收DTO class GroupResult { private String country; private Integer age; private List<UserItem> items; // getter、setter、构造方法省略 static class UserItem { private String name; private Integer age; // getter、setter、构造方法省略 } }
2. 聚合操作实现代码
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; import org.springframework.data.mongodb.core.query.Criteria; import java.util.List; public class ConditionalGroupService { private final MongoTemplate mongoTemplate; public ConditionalGroupService(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List<GroupResult> executeConditionalGroup() { // 1. 投影阶段:生成动态分组键groupKey,保留必要字段 ProjectionOperation project = Aggregation.project("name", "age", "country") .and(ConditionalOperators.when(Criteria.where("country").is(null)) .thenValueOf("age") .otherwiseValueOf("country")) .as("groupKey"); // 2. 分组阶段:按groupKey分组,收集数据并保留分组标识 GroupOperation group = Aggregation.group("groupKey") .first("country").as("country") .first("age").as("age") .push(Aggregation.fields() .and("name").as("name") .and(ConditionalOperators.when(Criteria.where("country").ne(null)) .thenValueOf("age") .then(null)) // country为null时不输出age字段 .as("age")) .as("items"); // 3. 最终投影:移除自动生成的_id字段,调整输出格式 ProjectionOperation finalProject = Aggregation.project("country", "age", "items") .andExclude("_id"); // 构建并执行聚合管道 Aggregation aggregation = Aggregation.newAggregation(project, group, finalProject); AggregationResults<GroupResult> results = mongoTemplate.aggregate( aggregation, "users", GroupResult.class); return results.getMappedResults(); } }
代码逻辑说明
- 投影阶段:通过
$cond判断country是否为null,生成统一的groupKey,作为后续分组的依据。 - 分组阶段:
- 按
groupKey分组,用first()获取分组的country和age标识(同一组内该字段值一致)。 - 用
push()收集组内用户数据,通过条件判断控制age字段的输出:仅当country不为null时保留该字段。
- 按
- 最终投影:移除MongoDB自动生成的
_id字段,确保输出格式符合预期。
注意事项
- 确保MongoDB版本在3.2及以上,支持
$cond等条件操作符。 - DTO的字段名需与聚合输出的字段严格匹配,Spring Data MongoDB会自动完成映射。
内容的提问来源于stack exchange,提问作者Doan Thang
相关产品推荐
相关产品推荐

