Перенос значения из старого поля в новое поле внутри вложенных документов в массиве для mongodb

#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"
        }
    ],
}
 

Мне нужно переместить значение из поля «подключение» в поле «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"
            }
        }
    ],
}
 

I have a microservice written in golang to work with the mongo database and inside the migration mechanism is implemented using the github.com/golang-migrate/migrate/ package (JSON is used as the request source for this package).

I did not have much experience with databases before and I had difficulties when working with nested documents inside an array for mongo.
First of all, I tried to solve the issue with the usual «update» and «set»:

 [
    {
        "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": {}
    }
]
 

На данный момент этот вариант работает, но есть большая проблема. Фактически, в каждом документе я воссоздаю поле «интерфейсы», и мне нужно явно указать все поля прикрепленного документа, иначе будет только новое поле. Это опасный момент, если в будущем в документе появятся новые поля и есть вероятность забыть указать новые поля при переносе.

Я был бы благодарен за помощь и советы о том, как улучшить запрос, чтобы при переносе данных в новое поле я не терял остальные поля.

Ответ №1:

Там был ответ. Использование $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": {}
    }
]