如何将MongoDB聚合查询转换为Spring Java实现?
在Spring中实现MongoDB重复msisdn字段的聚合查询
场景说明
我有如下Spring实体类:
@Document( collection = "users" ) public class UserEntity extends BaseEntity { @Indexed private String msisdn; ... }
在Mongo Shell中,我可以通过以下聚合查询找出重复的msisdn字段:
db.users.aggregate([ {"$group": { "_id": "$msisdn", "count": { "$sum": 1 } } }, {"$match": {"_id":{ "$ne": null } , "count": {"$gt": 1} } }, {"$project": {"msisdn": "$_id", "_id": 0} } ]);
该查询返回结果示例如下:
[ { msisdn: '123456' }, { msisdn: 'ABCDEF' } ]
我的问题是:如何在Spring中实现这个聚合查询?
实现方案
可以通过Spring Data MongoDB提供的Aggregation API实现对应聚合逻辑,以下是两种常见实现方式:
方式一:用Aggregation静态方法构建查询
注入MongoTemplate后,按Mongo Shell的聚合阶段依次构建查询:
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.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; import java.util.List; import java.util.Map; // 注入MongoTemplate private final MongoTemplate mongoTemplate; public List<Map<String, Object>> findDuplicateMsisdn() { // 1. $group阶段:按msisdn分组并统计数量 GroupOperation groupByMsisdn = Aggregation.group("msisdn") .count().as("count"); // 2. $match阶段:过滤msisdn非空且重复数大于1的分组 MatchOperation filterDuplicates = Aggregation.match( Criteria.where("_id").ne(null) .and("count").gt(1) ); // 3. $project阶段:重命名字段并隐藏原始_id ProjectionOperation projectFields = Aggregation.project() .and("_id").as("msisdn") .andExclude("_id"); // 组装聚合管道 Aggregation aggregation = Aggregation.newAggregation( groupByMsisdn, filterDuplicates, projectFields ); // 执行查询,返回Map类型结果 AggregationResults<Map<String, Object>> results = mongoTemplate.aggregate( aggregation, "users", Map.class ); return results.getMappedResults(); }
方式二:用TypedAggregation结合DTO接收结果
如果需要更明确的返回类型,可定义DTO来封装结果:
// 结果DTO public class DuplicateMsisdnDto { private String msisdn; // 构造器、getter/setter public DuplicateMsisdnDto(String msisdn) { this.msisdn = msisdn; } public String getMsisdn() { return msisdn; } public void setMsisdn(String msisdn) { this.msisdn = msisdn; } }
然后使用TypedAggregation指定输入实体类,直接映射到DTO:
import org.springframework.data.mongodb.core.aggregation.TypedAggregation; public List<DuplicateMsisdnDto> findDuplicateMsisdnWithDto() { // 构建聚合阶段,逻辑与方式一一致 GroupOperation groupByMsisdn = Aggregation.group("msisdn") .count().as("count"); MatchOperation filterDuplicates = Aggregation.match( Criteria.where("_id").ne(null) .and("count").gt(1) ); ProjectionOperation projectFields = Aggregation.project() .and("_id").as("msisdn") .andExclude("_id"); // 使用TypedAggregation绑定输入实体类 TypedAggregation<UserEntity> aggregation = Aggregation.newAggregation( UserEntity.class, groupByMsisdn, filterDuplicates, projectFields ); // 执行查询并映射到DTO AggregationResults<DuplicateMsisdnDto> results = mongoTemplate.aggregate( aggregation, DuplicateMsisdnDto.class ); return results.getMappedResults(); }
注意事项
- 两种方式完全对应Mongo Shell的聚合逻辑,仅API封装形式不同
- 使用DTO可避免Map类型的类型转换问题,返回结果更清晰
- 需确保
MongoTemplate已正确配置并连接到目标MongoDB实例
内容的提问来源于stack exchange,提问作者Ariel Malka
相关产品推荐
相关产品推荐

