Spring MongoDB 聚合中正确投影多个字段的实践方法

在 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 Map getUsersByAccount() {
    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 聚合查询的关键一步。