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