首页 > 解决方案 > 将值从旧字段迁移到 mongodb 数组中子文档内的新字段

问题描述

我在 mongo 集合的每个文档中都有一项任务,将值从一个字段迁移到另一个新字段。

数据最初外观的示例:

{
    "_id" : ObjectId("5ec402eca370d5834f18b762"),
    "serial" : "SN12345678",
    "hostname" : "router1",
    "interfaces" : [
        {
            "name" : "eth0",
            "type" : "sfp",
            "connection": "isp"
            "ip" : "192.168.1.1/24",
            "gateway" : "192.168.1.254",
        },
        {
            "name" : "eth1",
            "type" : "copper",
            "connection": "switch"
        }
    ],
}
{
    "_id" : ObjectId("1vb402hnk370d9520d18b333"),
    "serial" : "SN87654321",
    "hostname" : "switch1",
    "interfaces" : [
        {
            "name" : "eth0",
            "type" : "copper",
            "connection": "pc"
        },
        {
            "name" : "eth1",
            "type" : "copper",
            "connection": "printer"
        }
    ],
}

我需要将值从“connection”字段移动到每个集合文档中的“conenction_to”字段。最终结果应如下所示:

{
    "_id" : ObjectId("5ec402eca370d5834f18b762"),
    "serial" : "SN12345678",
    "hostname" : "router1",
    "interfaces" : [
        {
            "name" : "eth0",
            "type" : "copper",
            "connection_to": {
                "device_type": "isp"
            }
            "ip" : "192.168.1.1/24",
            "gateway" : "192.168.1.254",
        },
        {
            "name" : "eth1",
            "type" : "copper",
            "connection_to": {
                "device_type": "switch"
            }
        }
    ],
}
{
    "_id" : ObjectId("1vb402hnk370d9520d18b333"),
    "serial" : "SN87654321",
    "hostname" : "switch1",
    "interfaces" : [
        {
            "name" : "eth0",
            "type" : "copper",
            "connection_to": {
                "device_type": "pc"
            }
        },
        {
            "name" : "eth1",
            "type" : "copper",
            "connection_to": {
                "device_type": "printer"
            }
        }
    ],
}

我有一个用 golang 编写的微服务来与 mongo 数据库一起使用,并且内部迁移机制是使用 github.com/golang-migrate/migrate/ 包实现的(JSON 用作此包的请求源)。

我以前没有太多的数据库经验,并且在处理 mongo 数组中的嵌套文档时遇到了困难。首先,我尝试用通常的“更新”和“设置”来解决这个问题:

[
    {
        "update": "devices",
        "updates": [
            {
                "q": {"interfaces": {"$exists" : true}},
                "u": {
                    "$set": {
                        "interfaces.$[].connection_to": {
                            "device_type": "$connection"
                        }
                    }
                },
                "multi": true
            }
        ]
    }
]

...但我无法重用旧“连接”字段中的值。

我阅读了文档并发现必须使用聚合来重用字段值的信息。我收到这样的请求:

[
    {
        "aggregate": "devices",
        "pipeline": [
            {
                "$match" : {
                    "interfaces" : { "$exists":true }
                }
            },
            {
                "$set":{
                    "interfaces": {
                        "$map":{
                            "input": "$interfaces",
                            "as": "interface",
                            "in": {
                                "name": "$$interface.name",
                                "type": "$$interface.type",
                                "ip": "$$interface.ip",
                                "gateway": "$$interface.gateway",
                                "connection_to": {
                                    "device_type": "$$interface.connection"
                                }
                            }

                        }
                    }
                }
            },
            {
                "$out": "devices"
            }
        ],
        "cursor": {}
    }
]

目前此选项有效,但存在一个大问题。事实上,在每个文档中我都重新创建了“interfaces”字段,并且我必须明确指定附加文档的所有字段,否则只会有一个新字段。如果将来文档中出现新字段并且有可能忘记在迁移中指定新字段,这是一个危险的时刻。

我将不胜感激有关如何改进查询的帮助和建议,以便在将数据迁移到新字段时,我不会丢失其余字段。

标签: mongodb

解决方案


有一个答案。$mergeObjects的使用

[
    {
        "aggregate": "devices",
        "pipeline": [
            {
                "$match" : {
                    "interfaces" : { "$exists":true }
                }
            },
            {
                "$set":{
                    "interfaces": {
                        "$map":{
                            "input": "$interfaces",
                            "as": "interface",
                            "in": {
                                "$mergeObjects": [
                                    "$$interface",
                                    "connection_to": {
                                        "device_type": "$$interface.connection"
                                    }
                                ]
                            }
                        }
                    }
                }
            },
            {
                "$out": "devices"
            }
        ],
        "cursor": {}
    }
]

推荐阅读