
使用 Spring Data MongoDB 的 MongoTemplate 进行聚合时,Aggregation.group("field") 默认将分组键存入 _id 字段而非原始字段名,若未在后续 project() 阶段显式映射 _id → field,会导致 Java 对象中对应属性为 null。
使用 spring data mongodb 的 `mongotemplate` 进行聚合时,`aggregation.group("field")` 默认将分组键存入 `_id` 字段而非原始字段名,若未在后续 `project()` 阶段显式映射 `_id → field`,会导致 java 对象中对应属性为 `null`。
在 Spring Data MongoDB 中,Aggregation.group("name") 并不会直接生成 { "name": "$name" } 的输出结构,而是将分组值自动绑定到聚合结果的 _id 字段(MongoDB 原生行为)。因此,聚合流水线实际等价于:
[
{ "$match": { "modality": "check" } },
{ "$group": { "_id": "$name", "check_count": { "$sum": 1 } } },
{ "$project": { "name": "$_id", "check_count": 1, "_id": 0 } }
]而原代码中 Aggregation.project("name") 试图投影一个并不存在的 "name" 字段(此时分组结果中只有 _id 和 check_count),导致 Response.name 始终为 null。
✅ 正确做法是在 project() 阶段显式将 _id 映射为目标字段名:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("modality").is("check")),
Aggregation.group("name") // ← 分组键存入 _id
.count().as("check_count"),
Aggregation.project() // ← 关键:从 _id 投影出 name
.and("_id").as("name") // ✅ 正确映射
.and("check_count").as("check_count")
.andExclude("_id") // 可选:排除 _id,避免冗余
);
AggregationResults<Response> results = mongoTemplate.aggregate(aggregation, "user", Response.class);同时,请注意 Response 类中字段类型需严格匹配(如 check_count 应为 Long 或 Integer,而非 String):
public static class Response {
private String name; // 接收 _id 的字符串值
private Long check_count; // count() 返回的是 Long 类型
// 必须提供无参构造器 + getter/setter
public Response() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Long getCheck_count() { return check_count; }
public void setCheck_count(Long check_count) { this.check_count = check_count; }
}⚠️ 常见注意事项:
- Aggregation.group("field") 总是将分组表达式结果写入 _id,无论 "field" 是字符串字面量还是表达式;
- 若需按多个字段分组(如 name + status),应使用 group(Fields.from(...)),此时 _id 为嵌入文档,需用 "_id.name"、"_id.status" 等路径投影;
- project() 中调用 .and("xxx") 时,"xxx" 指的是上游输出字段名(即 _id、check_count),不是原始集合字段;
- 建议启用 AggregationOptions.withOptions(AggregationOptions.builder().allowDiskUse(true)) 应对大数据量聚合。
通过正确映射 _id 到业务字段,即可获得预期的结构化响应:
[
{ "name": "Alice", "check_count": 61 },
{ "name": "Bob", "check_count": 15 }
]










