
在 spring data mongodb 中使用 `aggregation` 时,若需在 `project()` 阶段同时保留 `_id` 映射字段和分组统计字段(如 `countoffoousers`),应避免链式调用 `.and("xxx")` 的无效写法,而须使用 `andinclude()` 显式包含目标字段。
Spring Data MongoDB 的 ProjectionOperation 提供了多种字段投影方式,但初学者常误以为连续调用 .and("field") 即可添加多个投影字段——实际上,and(String) 方法仅用于定义新计算字段或别名字段,而不会自动“包含”已存在于上游 pipeline(如 group())中的字段。你原代码中:
projectByIDandCount.and("countOfFooUsers"); // ❌ 无效:未指定别名,也未触发包含逻辑这行代码既未设置别名,也未触发字段保留,导致 countOfFooUsers 在投影后被丢弃,最终 MAandUsers.countOfFooUsers 为 null。
✅ 正确做法是使用 andInclude(...) —— 它专为透传上游已存在字段而设计,支持单个或多个字段名:
ProjectionOperation projectByIDandCount = project()
.and("_id").as("defaultMasterAccountId") // 将 _id 重命名为 defaultMasterAccountId
.andInclude("countOfFooUsers"); // 显式包含上游 group 生成的 countOfFooUsers 字段完整修正后的聚合逻辑如下:
public MapgetUsersByAccount() { MatchOperation filterByAccountId = match( new Criteria(ACCOUNT_ID).nin(Arrays.asList(null, "")) ); GroupOperation groupByMasterAccount = group(DEFAULT_MASTER_ACCOUNT_ID) .count().as("countOfFooUsers"); ProjectionOperation projectByIDandCount = project() .and("_id").as("defaultMasterAccountId") .andInclude("countOfFooUsers"); // ✅ 关键修复:确保 countOfFooUsers 被投出 Aggregation aggregation = newAggregation( filterByAccountId, groupByMasterAccount, projectByIDandCount ); AggregationResults result = mongoTemplate.aggregate( aggregation, USERS_COLLECTION, MAandUsers.class ); return result.getMappedResults().stream() .collect(Collectors.toMap( MAandUsers::getDefaultMasterAccountId, MAandUsers::getCountOfFooUsers )); }
⚠️ 注意事项:
- andInclude() 是唯一推荐用于透传非计算字段的方式;and("field") 必须配合 .as("alias") 使用,否则无实际效果;
- 确保实体类 MAandUsers 字段名与投影后的 key 完全一致(区分大小写),且有对应 getter/setter(Lombok @Data 已满足);
- 若需重命名多个字段,可连续使用 .and("oldName").as("newName");
- 调试时可通过 aggregation.toString() 打印生成的原生聚合管道,验证 $project 阶段结构是否符合预期。
掌握 andInclude() 与 and().as() 的分工,是构建可靠、可维护 MongoDB 聚合查询的关键一步。










