MongoDB聚合:基于两参数较大值排序并统计最大、最小及平均值
MongoDB聚合需求解决方案
示例文档
[ {"subdocument": {"value":100,"additionalValue":300}}, {"subdocument": {"value":100}} // 该文档不存在additionalValue字段 ]
需求目标
聚合后得到如下结果:
{ "largest": // 第一个完整文档,因为300是全局最大值 "smallest": // 第二个完整文档,因为100是最小值 "average": 150 // 第一个文档平均值为200,第二个为100,总体平均值150 }
已实现的聚合步骤
{ $sort: { 'subdocument.value': -1 } }, { $group: { _id: null, average: { $avg: '$subdocument.value' }, items: { $push: '$$ROOT' }, }, }, { $set: { largest: { $first: '$items' } } }, { $set: { smallest: { $last: '$items' } } }, { $project: { largest: 1, smallest: 1, average: 1 } },
当前问题
- 现有实现完全没考虑
additionalValue字段 - 不知道怎么用MongoDB表达式获取
subdocument.value和subdocument.additionalValue中的较大值(想要类似{ $max: { $larger: ['$subdocument.value', 'subdocument.additonalValue'] }}的效果) additionalValue是可选字段,部分文档可能没有这个字段
解决方案
要搞定这个需求,得先给每个文档算出两个值里的最大值,还有单文档的平均值,再基于这些数据做全局聚合,具体管道如下:
[ // 先给每个文档计算两个关键值:当前文档的最大值、当前文档的平均值 { $set: { docMax: { $max: [ "$subdocument.value", // 处理additionalValue不存在的情况,直接用value代替,不影响最大值计算 { $ifNull: ["$subdocument.additionalValue", "$subdocument.value"] } ] }, docAverage: { $avg: [ "$subdocument.value", { $ifNull: ["$subdocument.additionalValue", "$subdocument.value"] } ] } } }, // 全局聚合:算总体平均值,同时把所有文档和它们的最大值存起来 { $group: { _id: null, average: { $avg: "$docAverage" }, itemsWithMax: { $push: { doc: "$$ROOT", max: "$docMax" } } } }, // 找出最大值对应的文档,设为largest { $set: { largest: { $first: { $sortArray: { input: "$itemsWithMax", sortBy: { max: -1 } } }.doc } } }, // 找出最小值对应的文档,设为smallest { $set: { smallest: { $first: { $sortArray: { input: "$itemsWithMax", sortBy: { max: 1 } } }.doc } } }, // 清理输出,只保留需要的字段 { $project: { _id: 0, largest: 1, smallest: 1, average: 1 } } ]
关键点说明
$ifNull专门用来处理additionalValue缺失的情况,直接用value补位,避免计算报错- MongoDB里
$max本身就支持传数组参数,直接返回数组中的最大值,不需要自定义什么$larger函数 - 先计算每个文档的局部最大值和平均值,再进行全局聚合,这样既能准确筛选出对应最大/最小的文档,也能算出正确的总体平均值
内容的提问来源于stack exchange,提问作者Branchverse
相关产品推荐
相关产品推荐

