MongoDB聚合中如何按条件统计根状态与嵌套状态?
解决方案
要实现这个条件统计,核心是先把两种情况的状态数据统一成相同结构的数组,再进行后续的展开和分组操作。具体调整后的聚合管道如下:
聚合管道逻辑
- 匹配过滤:保留原有的
match条件,筛选需要统计的文档。 - 生成统一状态数组:添加一个临时字段
targetStatuses,通过条件判断生成对应数组:- 当
statuses数组为空时,生成仅包含根节点status的数组(结构和原statuses一致,方便后续处理) - 当
statuses不为空时,直接使用原statuses数组
- 当
- 展开数组:将
targetStatuses数组拆分为单个文档 - 分组统计:按
groupId和状态值分组,统计每个状态的出现次数
Java 代码实现
final List<DBObject> statusPipeline = Aggregation.newAggregation( match, // 保留原有的匹配条件 // 添加临时字段targetStatuses,统一两种情况的状态数据 Aggregation.addFields() .addFieldWithValue("targetStatuses", AggregationConditional.cond( ComparisonOperators.Eq.valueOf(ArrayOperators.Size.lengthOfArray("statuses")).equalToValue(0), // statuses为空时,生成包含根status的数组 Arrays.asList(new BasicDBObject("status", "$status")), // statuses不为空时,直接使用原数组 "$statuses" )).build(), // 展开临时数组 Aggregation.unwind("targetStatuses"), // 按groupId和status分组统计 Aggregation.group("$groupId", "$targetStatuses.status") .count().as("total"), // 可选:格式化结果为用户期望的键值对格式 Aggregation.replaceRoot(AggregationExpression.from( new BasicDBObject("$mergeObjects", Arrays.asList( new BasicDBObject("groupId", "$_id.groupId"), new BasicDBObject("$arrayToObject", Arrays.asList( Arrays.asList(new BasicDBObject("k", "$_id.status"), new BasicDBObject("v", "$total")) )) )) )) ) .toPipeline(Aggregation.DEFAULT_CONTEXT).stream() .map(BasicDBObject::new) .collect(Collectors.toList());
结果说明
- 对于
statuses为空的文档,会统计根节点的status(如UNPROVISIONED:1) - 对于
statuses不为空的文档,会统计数组中每个status的出现次数(如ACCEPTED:1、REMOVED:1) - 启用最后的
replaceRoot阶段后,结果会完全匹配用户期望的格式;若不需要该格式,可直接去掉该阶段,得到包含groupId、status、total的基础统计结构。
内容的提问来源于stack exchange,提问作者Castyr
相关产品推荐
相关产品推荐

