You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:35:34