You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"
        }
    }
])

步骤说明

  1. $unwind: "$samples":展开samples数组,因每个文档的samples仅含一个元素,展开后不会拆分数据。
  2. $addFields + $mergeObjects:
    • 依据Set1是否包含latitude或params字段,分别映射为location或device;
    • 若存在Set2数组,提取其第一个元素的params作为device(可根据实际需求调整优先级);
    • 用$mergeObjects合并所有匹配的键值对,自动忽略空对象。
  3. $project:将samples替换为处理后的结构,移除临时生成的processed字段。

注意事项

  • 若Set2数组可能包含多个元素,可使用$reduce或$map批量处理,当前默认取第一个元素;
  • 若存在同时包含latitude和params的异常Set1,可添加额外判断逻辑过滤或标记。

内容的提问来源于stack exchange,提问作者enavuio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 01:10:24