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

MongoDB聚合按年月(%m%Y)排序异常的解决方法咨询

问题解决建议

问题根源

你用%m%Y生成的是MMYYYY格式的字符串(比如012024),字符串排序是按字符逐个比较的——01开头的字符串比06开头的小,所以2024年的月份会被排在2023年之前,不符合时间顺序。

方案一:改用YYYYMM格式(最简单直接)

把日期格式化的格式从%m%Y改成%Y%m,生成YYYYMM格式的字符串(比如202306、202401),此时字符串排序的结果就是正确的时间顺序。

修改后的聚合管道代码:

Aggregation aggregation = Aggregation.newAggregation(
    match(Criteria.where(STATUS).nin("NEW", CANCELLED, REJECTED).and(DATE_CREATED).gte(startDate)),
    project(sumField)
        .andExpression("toDate(" + expression + ")")
        .as("formattedDate"),
    project(sumField)
        // 替换格式为%Y%m
        .and("formattedDate").dateAsFormattedString("%Y%m").as(MONTH),
    group(MONTH).sum(sumField).as(SUM),
    project(SUM).and(MONTH).as(DESCRIPTION),
    project(SUM).and(DESCRIPTION).previousOperation(),
    sort(Sort.by(Sort.Direction.ASC, DESCRIPTION))
);

AggregationResults<SumModel> results = mongoTemplate
    .aggregate(aggregation, collectionName, SumModel.class);
return results.getMappedResults();

如果需要最终返回的description还是MMYYYY格式,可以在排序后再做格式转换:

Aggregation aggregation = Aggregation.newAggregation(
    match(Criteria.where(STATUS).nin("NEW", CANCELLED, REJECTED).and(DATE_CREATED).gte(startDate)),
    project(sumField)
        .andExpression("toDate(" + expression + ")")
        .as("formattedDate"),
    // 保留原始日期,同时生成排序用的YYYYMM键
    project(sumField, "formattedDate")
        .and("formattedDate").dateAsFormattedString("%Y%m").as("sortKey"),
    group("sortKey", "formattedDate").sum(sumField).as(SUM),
    // 按YYYYMM键排序
    sort(Sort.by(Sort.Direction.ASC, "sortKey")),
    // 转换回MMYYYY格式作为description
    project(SUM)
        .and("formattedDate").dateAsFormattedString("%m%Y").as(DESCRIPTION)
);

方案二:按年份和月份数值排序

如果必须保留MMYYYY的分组键,可以在分组时同时提取年份和月份的数值,先按年份排序,再按月份排序:

修改后的聚合管道:

Aggregation aggregation = Aggregation.newAggregation(
    match(Criteria.where(STATUS).nin("NEW", CANCELLED, REJECTED).and(DATE_CREATED).gte(startDate)),
    project(sumField)
        .andExpression("toDate(" + expression + ")")
        .as("formattedDate"),
    project(sumField)
        .and("formattedDate").dateAsFormattedString("%m%Y").as(MONTH)
        // 提取年份、月份的数值用于排序
        .and("formattedDate").month().as("monthNum")
        .and("formattedDate").year().as("yearNum"),
    group(MONTH).sum(sumField).as(SUM)
        // 保留年份、月份数值到分组结果
        .first("yearNum").as("yearNum")
        .first("monthNum").as("monthNum"),
    // 先按年份升序,再按月份升序
    sort(Sort.by(Sort.Direction.ASC, "yearNum", "monthNum")),
    // 最终只保留需要的字段
    project(SUM).and(MONTH).as(DESCRIPTION)
);

这样排序时会先按年份从小到大,再按月份从小到大,既能得到正确的时间顺序,又能保留MMYYYY格式的description。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:59:56