You need to enable JavaScript to run this app. Spring Boot结合MongoDB聚合按季度参数统计订单数的实现问题
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot结合MongoDB聚合按季度参数统计订单数的实现问题

业务场景说明
  • 存储侧:MongoDB 订单集合的下单时间为ISO格式日期字段
  • 需求:按自定义季度维度统计订单数量,例如调用接口传入quarter=Q1时,需返回自定义规则下第一季度(前4个自然月)的订单统计结果
  • 技术栈:MongoDB聚合管道 + Spring Boot
  • 预期结果:匹配指定季度的订单条目需完整携带原有业务字段(id、userId、email、productName、productId等),同时包含对应季度的统计总数字段count
现有实现代码与异常表现

现有代码

@Override
public List<Order> demoByQuarter(String quarter) {
   MatchOperation matchOperation1 = 
   Aggregation.match(Criteria.where("months").lte(3));
        MatchOperation matchOperation2 = Aggregation.match(Criteria.where("months").gte(3));

        ConditionalOperators.Cond cond = ConditionalOperators.when(String.valueOf("Q1".equals(quarter)))
                .thenValueOf(String.valueOf(matchOperation1))
                .otherwiseValueOf(String.valueOf(matchOperation2));

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(where("date").ne(null)),
                Aggregation.project().and(month("$date")).as("months").and(cond).as("month"),
                Aggregation.group("month").count().as("count"),
                project("count").and("month").previousOperation());

        List<Demo> demo=  mongoTemplate.aggregate(aggregation, "user",Demo.class).getMappedResults();
        System.out.println(orders.size());
        return demo;
}

异常表现

  • 返回结果除count字段外,其余原有业务字段(id、userId、email等)全部为null
  • 统计范围覆盖数据库所有月份的订单,完全没有按照传入的季度参数做过滤
问题根因
  • 语法误用:错误将MatchOperation管道阶段操作当成普通字段表达式传入Cond条件判断,该写法完全不会触发实际的过滤逻辑,所以才会全量统计
  • 管道顺序错误:在提取月份字段后直接按月份做group分组,分组阶段会丢弃所有非分组键的原有文档字段,最终实体映射时自然丢失所有业务字段的值
  • 条件逻辑失效:when(String.valueOf("Q1".equals(quarter)))直接把布尔判断结果转成字符串作为条件,永远无法匹配预期的分支,逻辑完全失效
  • 逻辑偏离需求:需求是按传入的单个季度过滤全量数据统计,不需要按月份维度分组,原有的分组逻辑完全不符合业务目标
  • 代码笔误:打印语句中引用的orders变量未定义,会直接触发编译报错
正确实现代码

按业务规则自定义季度对应的月份范围(示例中Q1为1-4月,可按需调整)

@Override
public List<Order> demoByQuarter(String quarter) {
    // 根据入参匹配对应季度的月份范围
    int monthStart;
    int monthEnd;
    switch (quarter) {
        case "Q1":
            monthStart = 1;
            monthEnd = 4;
            break;
        // 其他季度可按规则扩展
        default:
            throw new IllegalArgumentException("非法的季度参数:" + quarter);
    }

    Aggregation aggregation = Aggregation.newAggregation(
        // 第一阶段:过滤无效数据 + 匹配当前季度的月份范围
        Aggregation.match(
            Criteria.where("date").ne(null)
                .andExpression(String.format(
                    "{$and: [{$gte: [{$month: '$date'}, %d]}, {$lte: [{$month: '$date'}, %d]}]}",
                    monthStart, monthEnd
                ))
        ),
        // 第二阶段:保留所有需要的原有业务字段,追加季度标识
        Aggregation.project()
            .and("id").as("id")
            .and("userId").as("userId")
            .and("email").as("email")
            .and("productName").as("productName")
            .and("productId").as("productId")
            .and(quarter).as("quarter"),
        // 第三阶段:通过窗口函数计算当前范围内的订单总数量,作为count字段填充到每条结果
        Aggregation.addFields()
            .addFieldWithValue("count",
                WindowOperation.function("count")
                    .of("*")
                    .window(WindowOperation.windows().documents(
                        WindowBounds.UNBOUNDED, WindowBounds.UNBOUNDED
                    ))
            )
            .build()
    );

    return mongoTemplate.aggregate(aggregation, "order", Order.class).getMappedResults();
}

如果需求仅需要返回单条季度汇总结果(不需要每条订单都携带原有业务字段),可将上面的project+addFields阶段替换为分组统计逻辑:

// 替换原有第二、三阶段为
Aggregation.group()
    .count().as("count")
    .first(quarter).as("quarter")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.05 09:45:04