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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 23:58:21