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

如何在Spring框架中用Java Aggregation类实现MongoDB年度统计查询

MongoDB聚合查询转Spring Data MongoDB Aggregation类实现

需求:将给定的MongoDB聚合查询转换为Spring Data MongoDB的Aggregation类实现,使用mongoTemplate.aggregate()方法,禁止使用AggregateIterable<Document>(因多数据库连接导致服务器加载缓慢)。

原MongoDB聚合查询

[
    {
        "$addFields": {
            "__alias_0": {
                "year": {
                    "$year": "$createdAt"
                }
            }
        }
    },
    {
        "$addFields": {
            "__count_by_value": {
                "k": "status",
                "v": "$status"
            }
        }
    },
    {
        "$group": {
            "_id": {
                "__alias_0": "$__alias_0",
                "__alias_1": "$__count_by_value.v"
            },
            "__alias_2": {
                "$sum": {
                    "$cond": [
                        {
                            "$ne": [
                                {
                                    "$type": "$status"
                                },
                                "missing"
                            ]
                        },
                        1,
                        0
                    ]
                }
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "__alias_0": "$_id.__alias_0",
            "__alias_1": "$_id.__alias_1",
            "__alias_2": 1
        }
    },
    {
        "$project": {
            "Date": "$__alias_0",
            "Number": "$__alias_2",
            "status": "$__alias_1",
            "_id": 0
        }
    },
    {
        "$group": {
            "_id": {
                "Date": "$Date"
            },
            "__grouped_docs": {
                "$push": "$$ROOT"
            }
        }
    },
    {
        "$sort": {
            "_id.Date.year": 1
        }
    },
    {
        "$unwind": "$__grouped_docs"
    },
    {
        "$replaceRoot": {
            "newRoot": "$__grouped_docs"
        }
    },
    {
        "$limit": 5000
    }
]

示例数据库数据

[
 {
  "year": {
         "$date": "2021-08-25T08:44:55.576Z"
  },
  "Status": "New"
},
{
  "year": {
         "$date": "2021-08-25T09:59:49.270Z"
  },
  "Status": "New"
},
{
  "year": {
         "$date": "2021-08-25T10:27:00.003Z"
  },
  "Status": "Rejected"
},
{
  "year": {
         "$date": "2022-01-10T08:12:25.816Z"
  },
  "Status": "Rejected"
},
{
  "year": {
         "$date": "2022-01-10T08:13:07.423Z"
  },
  "Status": "Rejected"
}
]

期望查询结果

[
 {
  "year": 2021,
  "Status": "New",
  "Amount": 2
 },
 {
  "year": 2021,
  "Status": "Rejected",
  "Amount": 1
 },
 {
  "year": 2022,
  "Status": "Rejected",
  "Amount": 2
 }
]

Java Aggregation类实现代码

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import java.util.List;

// 结果映射类,字段与期望结果对应
class StatusYearStats {
    private int year;
    private String Status;
    private int Amount;

    // Getters & Setters
    public int getYear() { return year; }
    public void setYear(int year) { this.year = year; }
    public String getStatus() { return Status; }
    public void setStatus(String status) { Status = status; }
    public int getAmount() { return Amount; }
    public void setAmount(int amount) { Amount = amount; }
}

public class YearlyStatusAggregation {

    private final MongoTemplate mongoTemplate;

    public YearlyStatusAggregation(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    public List<StatusYearStats> getYearlyStatusCount() {
        // 1. 提取日期字段的年份到__alias_0
        // 注:如果实际日期字段是示例中的"year",替换'$createdAt'为'$year'
        AddFieldsOperation addYearAlias = AddFieldsOperation.addField("__alias_0")
                .withValue(AggregationExpression.from(MongoExpression.create("{ year: { $year: '$createdAt' } }")));

        // 2. 构造状态映射对象__count_by_value
        AddFieldsOperation addCountByValue = AddFieldsOperation.addField("__count_by_value")
                .withValue(AggregationExpression.from(MongoExpression.create("{ k: 'status', v: '$status' }")));

        // 3. 按年份+状态分组统计数量(排除status字段缺失的文档)
        GroupOperation groupByYearStatus = Aggregation.group("__alias_0", "__count_by_value.v")
                .sum(AggregationExpression.from(MongoExpression.create(
                        "{ $cond: [ { $ne: [ { $type: '$status' }, 'missing' ] }, 1, 0 ] }"
                ))).as("__alias_2");

        // 4. 第一次投影:重组分组后的字段
        ProjectionOperation project1 = Aggregation.project()
                .and("_id.__alias_0").as("__alias_0")
                .and("_id.__alias_1").as("__alias_1")
                .and("__alias_2").as("__alias_2")
                .andExclude("_id");

        // 5. 第二次投影:重命名为目标字段名
        ProjectionOperation project2 = Aggregation.project()
                .and("__alias_0").as("Date")
                .and("__alias_2").as("Number")
                .and("__alias_1").as("status")
                .andExclude("_id");

        // 6. 按年份分组,聚合同一年的所有状态统计文档
        GroupOperation groupByDate = Aggregation.group("Date")
                .push("$$ROOT").as("__grouped_docs");

        // 7. 按年份升序排序
        SortOperation sortByYear = Aggregation.sort(Sort.Direction.ASC, "_id.Date.year");

        // 8. 展开分组后的文档数组
        UnwindOperation unwindDocs = Aggregation.unwind("__grouped_docs");

        // 9. 将展开后的文档设为根文档
        ReplaceRootOperation replaceRoot = Aggregation.replaceRoot("__grouped_docs");

        // 10. 限制返回结果数量
        LimitOperation limit = Aggregation.limit(5000);

        // 组装聚合管道
        Aggregation aggregation = Aggregation.newAggregation(
                addYearAlias, addCountByValue, groupByYearStatus,
                project1, project2, groupByDate,
                sortByYear, unwindDocs, replaceRoot, limit
        );

        // 执行聚合并映射到结果类
        return mongoTemplate.aggregate(aggregation, "your_collection_name", StatusYearStats.class).getMappedResults();
    }
}

注意事项

  • 替换代码中的your_collection_name为实际MongoDB集合名称。
  • 若日期字段是示例数据中的year而非原查询的createdAt,需修改第一个AddFieldsOperation中的'$createdAt'为'$year'。
  • 结果类StatusYearStats的字段名需与期望结果严格对应(注意大小写)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:40:27