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

Spring Boot MongoTemplate聚合投影无法新增字段问题求助

解决MongoDB聚合查询新增投影字段的问题

问题分析

你遇到的核心问题是:

  1. 当把awardForContentId加入group分组键后,MongoDB会将分组结果的_id生成为包含userId和awardForContentId的对象,而你的映射类注解配置错误,导致userId被映射为整个_id对象,awardForContentId字段无法正确匹配。
  2. 直接在project阶段引用awardForContentId报错,是因为group阶段后,该字段仅存在于_id对象内部,不能直接顶层引用。

解决方案1:按userId+awardForContentId分组(每个内容的积分单独统计)

如果需要统计用户针对不同内容获得的礼品积分(即同一个用户对应多条不同awardForContentId的记录),可以通过project阶段提取_id内的字段到顶层,配合正确的映射类实现。

步骤1:修正映射类

移除错误的@Id和@Field("id")注解,让字段直接匹配投影后的顶层字段:

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RecentlyRewardedUsers {
    private String userId;
    private BigDecimal totalValueAwarded;
    private String awardForContentId;
}

步骤2:调整聚合查询

在group之后添加project阶段,将_id中的userId和awardForContentId提取为顶层字段:

Aggregation agg = Aggregation.newAggregation(
    // 过滤近range天的GIFT类型交易
    match(Criteria.where("createdDate").gt(LocalDate.now().minusDays(range))
                  .and("transactionAction").is("GIFT")),
    // 按userId和awardForContentId分组,统计总积分
    group("userId", "awardForContentId")
        .sum("transactionAmount").as("totalValueAwarded"),
    // 投影:提取_id内的字段到顶层
    project()
        .and("_id.userId").as("userId")
        .and("_id.awardForContentId").as("awardForContentId")
        .and("totalValueAwarded").as("totalValueAwarded"),
    // 分页逻辑
    skip((long) pageable.getPageNumber() * pageable.getPageSize()),
    limit(pageable.getPageSize())
);

List<RecentlyRewardedUsers> results = mongoTemplate.aggregate(agg, Transaction.class, RecentlyRewardedUsers.class).getMappedResults();

解决方案2:按userId分组(每个用户仅一条记录,带最新的awardForContentId)

如果只需要每个用户一条记录,同时带上该用户最近获得礼品对应的awardForContentId,可以在group阶段使用first()方法获取最新的内容ID,无需将其作为分组键。

步骤1:修正映射类

使用@Id注解将_id(即分组后的userId值)映射到userId字段:

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RecentlyRewardedUsers {
    @Id
    private String userId;
    private BigDecimal totalValueAwarded;
    private String awardForContentId;
}

步骤2:调整聚合查询

先按createdDate降序排序,确保最新的交易排在前面,再通过first()方法获取最新的awardForContentId:

Aggregation agg = Aggregation.newAggregation(
    // 过滤条件
    match(Criteria.where("createdDate").gt(LocalDate.now().minusDays(range))
                  .and("transactionAction").is("GIFT")),
    // 按创建时间降序,确保最新交易优先
    sort(Sort.Direction.DESC, "createdDate"),
    // 按userId分组,统计总积分并取最新的awardForContentId
    group("userId")
        .sum("transactionAmount").as("totalValueAwarded")
        .first("awardForContentId").as("awardForContentId"),
    // 分页逻辑
    skip((long) pageable.getPageNumber() * pageable.getPageSize()),
    limit(pageable.getPageSize())
);

List<RecentlyRewardedUsers> results = mongoTemplate.aggregate(agg, Transaction.class, RecentlyRewardedUsers.class).getMappedResults();

内容的提问来源于stack exchange,提问作者MetaCoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 18:15:35