MongoDB:如何聚合未知键的对象字段值为带键的数组集合?
MongoDB动态标签聚合:合并未知键为值数组
需求说明
有一批文档,每个文档的tags字段包含动态的键值对(键名无法提前确定),需要将所有文档聚合为一个,让每个标签键对应一个包含所有出现过的值的去重数组。
输入示例
{ "_id" : ObjectId("6346826e3b4cd5a90b20b75b"), "tags" : { "planet" : "earth", "color" : "blue" } } { "_id" : ObjectId("634683723b4cd5a90b20b819"), "tags" : { "planet" : "earth", "color" : "green", "foo" : "bar" } } { "_id" : ObjectId("634683823b4cd5a90b20b829"), "tags" : { "foo" : "acme", "color" : "red", "xyz" : "1" } }
预期输出
{ "_id" : ObjectId("634684063b4cd5a90b20b87f"), "tags" : { "planet" : [ "earth" ], "color" : [ "blue", "green", "red" ], "foo" : [ "bar", "acme" ], "xyz" : [ "1" ] } }
解决方案
使用MongoDB聚合框架,通过以下管道实现:
db.collection.aggregate([ // 1. 把tags对象转成键值对数组,处理动态键 { $project: { tagPairs: { $objectToArray: "$tags" } } }, // 2. 展开每个键值对,让单个标签键值对独立成文档 { $unwind: "$tagPairs" }, // 3. 按标签键分组,收集并去重对应的值 { $group: { _id: "$tagPairs.k", values: { $addToSet: "$tagPairs.v" } } }, // 4. 把所有标签的键值对聚合到一个数组中 { $group: { _id: null, tags: { $push: { k: "$_id", v: "$values" } } } }, // 5. 把数组转回对象,同时设置自定义_id { $project: { _id: ObjectId("634684063b4cd5a90b20b87f"), tags: { $arrayToObject: "$tags" } } } ])
步骤解释
- $project + $objectToArray:将
tags对象转换为[{k: "键名", v: "值"}, ...]的数组结构,解决动态键无法直接处理的问题。 - $unwind:拆分数组,让每个标签键值对单独成为一个文档,方便后续分组操作。
- $group(第一次):按标签键分组,用
$addToSet自动去重并收集所有对应的值。 - $group(第二次):将所有标签的分组结果合并成一个数组,为转回对象做准备。
- $project + $arrayToObject:把数组重新转换为
tags对象,同时设置预期的_id(若不需要固定_id,可改为null或直接省略该字段)。
内容的提问来源于stack exchange,提问作者Bernd
相关产品推荐
相关产品推荐

