MongoDB聚合管道:将前阶段数组结果转换为对象
将MongoDB聚合结果数组转为单个对象(使用$project + $reduce)
首先咱们得理清逻辑:你的$match阶段返回的是多个独立文档,要把它们转成单个对象,第一步得先把这些文档收集到一个数组里(不然$reduce没东西可遍历),接着再用$reduce来处理这个数组。
这里给你完整的管道实现,在$match之后的后续阶段如下:
const pipeline = [ { $match: query }, // 你的第一个管道阶段 // 阶段2:把所有匹配的文档收集到一个数组中 { $group: { _id: null, // 不按任何字段分组,将所有结果聚合成一组 documents: { $push: "$$ROOT" } // 把每个完整文档推到documents数组里 } }, // 阶段3:用$project结合$reduce将数组转为单个对象 { $project: { _id: 0, // 移除自动生成的_id字段 result: { $reduce: { input: "$documents", // 指定要遍历的目标数组 initialValue: {}, // 累加器的初始状态设为空对象 in: { // 将当前遍历到的文档合并到累加器对象中 $mergeObjects: ["$$value", "$$this"] } } } } } ];
关键部分解释:
$group阶段:因为$match返回的是分散的文档,必须先把它们打包成数组才能用$reduce处理。_id: null表示不按任何字段分组,把所有结果归为一组;$push: "$$ROOT"会把每个完整的文档添加到documents数组里。$project中的$reduce:input指定要遍历的数组就是我们刚收集的documentsinitialValue是累加器的初始状态,这里设为空对象in是每次遍历执行的逻辑,用$mergeObjects把当前文档($$this)和累加器里的对象($$value)合并。如果有重复字段,后面的文档会覆盖前面的(比如你的示例里,第二个文档的score和views会替换第一个的)。
如果需要聚合数值(比如求和score和views):
要是你不是单纯合并对象,而是想对相同author的数值做聚合(比如求和),可以修改$reduce的in逻辑:
{ $project: { _id: 0, result: { $reduce: { input: "$documents", initialValue: { author: "", score: 0, views: 0 }, in: { author: "$$this.author", // 示例中author都是dave,直接取当前值即可 score: { $add: ["$$value.score", "$$this.score"] }, views: { $add: ["$$value.views", "$$this.views"] } } } } } }
这样得到的结果会是:{ result: { author: "dave", score: 165, views: 621 } }
注意事项:
- 如果文档里有重复字段且不想被覆盖,得提前规划好合并逻辑(比如把重复字段转成数组)
$mergeObjects只会合并顶层字段,如果有嵌套对象,需要额外处理
内容的提问来源于stack exchange,提问作者Sandeep Sharma
相关产品推荐
相关产品推荐

