如何将MongoDB聚合管道中$group的最值结果合并回原始文档
MongoDB 按level分组计算最值并合并回原始文档
需求说明
按level字段分组,计算每组内x、y字段的最大值和最小值,将这些结果以动态命名的字段(如max_level_6_x)添加到对应level的每一条原始文档中,保留原始文档的所有字段供后续聚合阶段使用。
解决方案
方法一:使用$setWindowFields(MongoDB 5.0+ 推荐)
该方法无需分组后再关联,直接在原始文档上添加分组统计值,性能更优:
db.collection.aggregate([ { $setWindowFields: { partitionBy: "$level", output: { stats: { $mergeObjects: { max_x: { $max: "$x" }, min_x: { $min: "$x" }, max_y: { $max: "$y" }, min_y: { $min: "$y" } } } } } }, { $set: { dynamicStats: { $arrayToObject: [ [ { k: { $concat: ["max_level_", "$level", "_x"] }, v: "$stats.max_x" }, { k: { $concat: ["min_level_", "$level", "_x"] }, v: "$stats.min_x" }, { k: { $concat: ["max_level_", "$level", "_y"] }, v: "$stats.max_y" }, { k: { $concat: ["min_level_", "$level", "_y"] }, v: "$stats.min_y" } ] ] } } }, { $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$dynamicStats"] } } }, { $unset: ["stats", "dynamicStats"] } ])
阶段解释:
$setWindowFields:按level分区,计算每个分区内x、y的最值并存入临时stats字段,保留所有原始文档。$set:通过$concat生成目标字段名(如max_level_6_x),结合统计值构造dynamicStats对象。$replaceRoot:将原始文档与dynamicStats合并,作为新的根文档。$unset:移除临时字段,得到最终结构。
方法二:兼容旧版本(MongoDB 3.2+)
如果使用低于5.0的MongoDB版本,可通过$group+$unwind+$replaceRoot实现:
db.collection.aggregate([ // 分组计算最值并保存原始文档列表 { $group: { _id: "$level", max_x: { $max: "$x" }, min_x: { $min: "$x" }, max_y: { $max: "$y" }, min_y: { $min: "$y" }, docs: { $push: "$$ROOT" } } }, // 展开原始文档列表 { $unwind: "$docs" }, // 合并原始文档与统计字段 { $replaceRoot: { newRoot: { $mergeObjects: [ "$docs", { $arrayToObject: [ [ { k: { $concat: ["max_level_", "$_id", "_x"] }, v: "$max_x" }, { k: { $concat: ["min_level_", "$_id", "_x"] }, v: "$min_x" }, { k: { $concat: ["max_level_", "$_id", "_y"] }, v: "$max_y" }, { k: { $concat: ["min_level_", "$_id", "_y"] }, v: "$min_y" } ] ] } ] } } } ])
阶段解释:
$group:按level分组计算最值,同时用$push将该分组的所有原始文档存入docs数组。$unwind:将docs数组展开,每条原始文档单独成为一个文档,携带分组的最值数据。$replaceRoot:把原始文档与动态生成的统计字段合并,得到目标结构。
内容的提问来源于stack exchange,提问作者Shaun
相关产品推荐
相关产品推荐

