MongoDB中值为数组的Map类型字段聚合查询实现方案
MongoDB聚合实现两种联系方式视图输出
需求1:按联系方式类型生成独立字段的输出
对应示例中每个联系方式单独成文档,字段名对应联系方式类型的聚合管道:
db.collection.aggregate([ // 将contactInfo转换为k-v数组,空Map转为空数组 { $addFields: { contactEntries: { $cond: { if: { $eq: ["$contactInfo", {}] }, then: [], else: { $objectToArray: "$contactInfo" } } } } }, // 拆分每个联系方式类型,保留空/无数据的文档 { $unwind: { path: "$contactEntries", preserveNullAndEmptyArrays: true } }, // 拆分每个联系方式值(数组拆为单个元素),保留空数组的情况 { $unwind: { path: "$contactEntries.v", preserveNullAndEmptyArrays: true } }, // 合并姓名与动态联系方式字段,处理空数据场景 { $project: { _id: 0, result: { $mergeObjects: [ { name: "$name" }, { $cond: { if: "$contactEntries", then: { $arrayToObject: [[{ k: { $toLower: "$contactEntries.k" }, v: "$contactEntries.v" }]] }, else: {} } } ] } } }, // 替换根节点为结果文档 { $replaceRoot: { newRoot: "$result" } }, // 去重,避免空contactInfo时生成重复的仅姓名文档 { $group: { _id: "$name", doc: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$doc" } } ])
关键步骤说明:
$objectToArray将Map结构的contactInfo转为k-v数组,空Map通过$cond转为空数组,避免后续操作报错。- 两次
$unwind分别拆分联系方式类型和数组元素,preserveNullAndEmptyArrays保证空数据文档不被过滤。 $arrayToObject动态生成联系方式字段,$toLower将类型名转为小写(如Email→email),不需要可移除。- 最终
$group去重,解决空contactInfo场景下重复生成仅姓名文档的问题。
需求2:统一格式输出contactType和contactInfo字段
对应要求的{name, contactType, contactInfo}格式的聚合管道:
db.collection.aggregate([ { $addFields: { contactEntries: { $cond: { if: { $eq: ["$contactInfo", {}] }, then: [], else: { $objectToArray: "$contactInfo" } } } } }, { $unwind: { path: "$contactEntries", preserveNullAndEmptyArrays: true } }, { $unwind: { path: "$contactEntries.v", preserveNullAndEmptyArrays: true } }, // 生成统一格式字段 { $project: { _id: 0, name: "$name", contactType: { $cond: { if: "$contactEntries", then: { $toLower: "$contactEntries.k" }, else: null } }, contactInfo: "$contactEntries.v" } }, // 处理空数据场景,仅保留姓名 { $addFields: { result: { $cond: { if: "$contactEntries", then: { name: "$name", contactType: "$contactType", contactInfo: "$contactInfo" }, else: { name: "$name" } } } } }, { $replaceRoot: { newRoot: "$result" } }, // 去重,避免重复文档 { $group: { _id: { name: "$name", contactType: "$contactType", contactInfo: "$contactInfo" }, doc: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$doc" } } ])
关键步骤说明:
- 前半部分拆分逻辑与需求1一致,确保所有边界场景被覆盖。
$project直接生成contactType和contactInfo字段,空数据场景下contactType设为null,也可根据需求调整。$cond判断是否存在有效联系方式,空场景下仅保留姓名字段。
内容的提问来源于stack exchange,提问作者Raman
相关产品推荐
相关产品推荐

