使用MongoDB聚合结合Spring Data MongoTemplate统计各分类文档数量
问题根因拆解
你之前编写的聚合代码无法得到正确结果,核心是四个逻辑错漏:
- 未处理数组字段的拆分逻辑:Mongo的分组操作是按文档的字段值匹配,直接对作为数组的
category字段分组时,会把整个数组作为分组键值,根本不会逐元素统计每个分类的出现次数。比如同时包含cat1、cat2的文档会被单独归为一个值为["cat1","cat2"]的分组,和你要的逐分类统计逻辑完全不符 - 聚合管道顺序颠倒:你把match过滤操作放在了group阶段之后,会造成全集合扫描做无意义的分组计算,性能极差,过滤类操作应该尽量放在管道最前面,减少后续环节的处理数据量
- 投影阶段的字段映射错误:经过拆分数组、分组操作后,分组逻辑输出的分类值是存在聚合自带的
_id字段里的,不是一个叫category的独立字段,你原来的投影直接取category字段是取不到值的 - 类型不匹配的小问题:代码里写的返回泛型是
PIICategoryCount,但实际传入的目标映射类是CountModel.class,会触发类型不匹配的运行时错误
正确实现方案
聚合管道按照「过滤→拆分数组→分组统计→字段映射」的顺序编写,对应Spring Data MongoTemplate的代码如下:
// 1. 最先执行过滤逻辑(保留你原有的match条件即可) MatchOperation matchOperation = match(/* 你的业务过滤条件 */); // 2. 拆分category数组:将每篇文档的数组元素拆成独立的单元素文档 // 如果需要兼容无category字段、空数组的文档,可改为unwind("category", true) UnwindOperation unwindCategory = unwind("category"); // 3. 按拆分后的单个分类值分组,统计每个分类关联的文档总数 GroupOperation groupByCategory = group("category").count().as("categoryCount"); // 4. 投影映射,对齐CountModel的字段结构 ProjectionOperation projectionToModel = project() .and("_id").as("category") // 取分组的_id(即单个分类值)映射为category字段 .and("categoryCount").as("count"); // 取统计值映射为count字段 // 管道顺序严格按照流程排列,避免性能损耗 Aggregation aggregation = newAggregation(matchOperation, unwindCategory, groupByCategory, projectionToModel); AggregationResults<CountModel> results = mongoTemplate.aggregate(aggregation, "repo_name", CountModel.class); return results.getMappedResults();
常见注意点
- 如果某篇文档的category字段包含重复值(比如同一个文档里category是["cat1","cat1"]),默认unwind会把这个文档拆成两条,统计时cat1的计数会+2。如果需要按文档去重统计(同一文档里多次出现同一分类只算1次),需要在unwind后加一层去重逻辑
- 如果不设置unwind的保留空数组参数,所有没有category字段、category为空数组的文档会被直接过滤,不会出现在最终统计结果里,按需调整即可
内容的提问来源于stack exchange,提问作者Siva Rahul
相关产品推荐
相关产品推荐

