MongoDB文档统一结构重构:属性重命名与数组展开
MongoDB 异构文档结构统一聚合方案
问题背景
集合中文档结构不一致:部分文档的samples数组里包含Set1(存储位置数据)和Set2(存储设备数据),另一部分文档仅含Set1但存储的是设备数据。需要将Set1/Set2根据内容重命名为location/device,并统一samples为对象结构,确保不丢失或重复数据。
原始数据
[ { _id: ObjectId("123ab5fc3065ff2337479d89"), samples: [ { Set1: { "latitude": "12.123" }, Set2: [ { params: {"device": "456"} } ] } ] }, { _id: ObjectId("123ab4fc3065ff2337479d89"), samples: [ { Set1: { params: {"device": "123"} } } ] } ]
期望结果
[ { _id: ObjectId("123ab5fc3065ff2337479d89"), samples: { device: { params: {"device": "456"} }, location: {"latitude": "12.123" } } }, { _id: ObjectId("123ab4fc3065ff2337479d89"), samples: { device: { params: {"device": "123"} } } } ]
当前尝试
曾用$unwind展开samples但遇到结构匹配问题,转而尝试$facet但方向不明确,当前聚合语句如下:
db.getCollection("collection").aggregate( [ { $unwind: "$samples" }, { $facet: { set2: [{ $unwind: "$samples.set2" }], set1: [ ] } } ] )
解决方案
无需使用$facet,可通过判断字段特征区分数据类型,再重组结构:
完整聚合管道
db.getCollection("collection").aggregate([ // 展开samples数组(每个文档的samples仅含一个元素,展开不影响数据完整性) { $unwind: "$samples" }, // 处理Set1/Set2,根据字段特征映射为location/device { $addFields: { "samples.processed": { $mergeObjects: [ // 检测Set1是否为位置数据(含latitude字段) { $cond: [ { $ifNull: ["$samples.Set1.latitude", false] }, { location: "$samples.Set1" }, {} ] }, // 检测Set1是否为设备数据(含params字段) { $cond: [ { $ifNull: ["$samples.Set1.params", false] }, { device: "$samples.Set1" }, {} ] }, // 处理Set2数组,提取第一个元素的设备数据 { $cond: [ { $ifNull: ["$samples.Set2", false] }, { device: { $arrayElemAt: ["$samples.Set2.params", 0] } }, {} ] } ] } } }, // 替换samples为处理后的结构,清理临时字段 { $project: { samples: "$samples.processed" } } ])
步骤说明
$unwind: "$samples":展开samples数组,因每个文档的samples仅含一个元素,展开后不会拆分数据。$addFields+$mergeObjects:- 依据
Set1是否包含latitude或params字段,分别映射为location或device; - 若存在
Set2数组,提取其第一个元素的params作为device(可根据实际需求调整优先级); - 用
$mergeObjects合并所有匹配的键值对,自动忽略空对象。
- 依据
$project:将samples替换为处理后的结构,移除临时生成的processed字段。
注意事项
- 若
Set2数组可能包含多个元素,可使用$reduce或$map批量处理,当前默认取第一个元素; - 若存在同时包含
latitude和params的异常Set1,可添加额外判断逻辑过滤或标记。
内容的提问来源于stack exchange,提问作者enavuio
相关产品推荐
相关产品推荐

