MongoDB聚合$sum带条件:缺失属性时取消求和的实现方法
解决方案:按组全字段存在性判断后求和
当然可行!你的需求核心是仅当组内所有文档都包含目标字段时才求和,否则返回null,而MongoDB默认的$sum操作会把缺失字段当作0处理,这就是为什么你的初始查询无法满足要求。我们需要通过额外的阶段来判断组内字段的存在性,再决定是否返回求和结果。
实现思路
- 标记字段存在性:先给每个文档添加标记,说明它是否包含
length和area字段; - 分组统计与校验:按
name分组时,同时统计组内是否所有文档都包含目标字段(用$min判断,因为布尔值true对应1,false对应0,$min为1则表示全组都有该字段); - 条件返回结果:根据校验结果,返回求和值或
null。
完整Mongoose聚合代码
let query = mongoose.model('mycollection').aggregate([ // 阶段1:标记每个文档是否包含length/area字段 { $project: { name: 1, length: 1, area: 1, hasLength: { $exists: ["$length", true] }, hasArea: { $exists: ["$area", true] } } }, // 阶段2:分组统计,同时校验全组字段存在性 { $group: { _id: { name: "$name" }, count: { $sum: 1 }, allHaveLength: { $min: "$hasLength" }, // 1=全组都有length,0=至少一个缺失 allHaveArea: { $min: "$hasArea" }, // 1=全组都有area,0=至少一个缺失 totalLength: { $sum: "$length" }, totalArea: { $sum: "$area" } } }, // 阶段3:根据校验结果返回求和值或null { $project: { count: 1, summarizedLength: { $cond: ["$allHaveLength", "$totalLength", null] }, summarizedArea: { $cond: ["$allHaveArea", "$totalArea", null] } } } ]);
结果验证
针对你提供的测试数据:
[ { name: 'abc', length: 25, area: 10 }, { name: 'abc', length: 5 } ]
执行上述查询后,会得到你期望的输出:
[ { count: 2, summarizedLength: 30, summarizedArea: null, _id: { name: 'abc' } } ]
补充说明
- 如果希望缺失时不显示该字段而非返回
null,可以将$cond的else分支改为$$REMOVE(MongoDB 3.6+支持),比如:summarizedArea: { $cond: ["$allHaveArea", "$totalArea", "$$REMOVE"] } - 用
$min判断全组字段存在性比$allElementsTrue更高效,因为不需要存储整个组的标记数组,直接通过最小值判断即可。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

