Обновить в forEach на оболочке mongodb
У меня есть коллекция aTable с 2 записями:
{
"title" : "record 1",
"fields" : [
{
"_id" : 1,
"items" : [
1
]
},
{
"_id" : 2,
"items" : [
2,3,4
]
},
{
"_id" : 3,
"items" : [
5
]
}
]
},
{
"title" : "record 2",
"fields" : [
{
"_id" : 4,
"items" : [
7,8,9,10
]
},
{
"_id" : 5,
"items" : [
]
},
{
"_id" : 6,
"items" : [
11,12
]
}
]
}
Я хочу обновить поля aTable.fields.items из
items : [ 11,12 ]
в
items : [
{item: 11, key: 0},
{item:12, key: 0}
]
Я просматриваю поля с помощью forEach, но не могу сохранить его:
var t = db.aTable.find();
t.forEach(function( aRow ) {
aRow.fields.forEach( function( aField ){
aField.items.forEach( function( item ){
var aNewItem = { item: parseInt(item), ref: 0 };
db.aTable.update(item, {$set:aNewItem})
} )
} )
});
Ответы
Ответ 1
Чтобы получить то, что вы хотите, вам понадобятся несколько вещей:
t.forEach(function( aRow ) {
var newFields = [];
aRow.fields.forEach( function( aField ){
var newItems = [];
aField.items.forEach( function( item ){
var aNewItem = { item: parseInt(item), ref: 0 };
newItems.push( aNewItem );
} );
newFields.push({ _id: aField._id, items: newItems });
} )
aTable.update(
{ _id: aRow._id },
{ "$set": { "fields": newFields } }
);
});
Поэтому в основном вам нужно "перестроить" свои массивы перед обновлением
Ответ 2
Вы можете вносить изменения непосредственно во весь объект, а затем сохранять его. Попробуйте следующий фрагмент
db.aTable.find().forEach(function (itemWrapper){
itemWrapper.fields.forEach(function(field){
var items = field.items;
var newItems = [];
items.forEach(function(item){
var t = {'item':item,'key':0}
newItems.push(t);
})
field.items = newItems;
})
db.aTable.save(itemWrapper)
})
То, что я делаю, это итерация по всем элементам и создание нового массива с {item: 1, key:0}
а затем установка его обратно в массив элементов в полевом объекте.
Это результат после обновления:
{
"_id" : ObjectId("5332a192ece4ce8362c7a553"),
"title" : "record 1",
"fields" : [
{
"_id" : 1,
"items" : [
{
"item" : 1,
"key" : 0
}
]
},
{
"_id" : 2,
"items" : [
{
"item" : 2,
"key" : 0
},
{
"item" : 3,
"key" : 0
},
{
"item" : 4,
"key" : 0
}
]
},
{
"_id" : 3,
"items" : [
{
"item" : 5,
"key" : 0
}
]
}
]
}
/* 1 */
{
"_id" : ObjectId("5332a192ece4ce8362c7a554"),
"title" : "record 2",
"fields" : [
{
"_id" : 4,
"items" : [
{
"item" : 7,
"key" : 0
},
{
"item" : 8,
"key" : 0
},
{
"item" : 9,
"key" : 0
},
{
"item" : 10,
"key" : 0
}
]
},
{
"_id" : 5,
"items" : []
},
{
"_id" : 6,
"items" : [
{
"item" : 11,
"key" : 0
},
{
"item" : 12,
"key" : 0
}
]
}
]
}
Ответ 3
Начиная с Mongo 4.2
, db.collection.update()
может принять конвейер агрегации, наконец, разрешив обновление поля на основе его текущего значения и, таким образом, используя запрос вместо javascript:
// {
// title: "record 1",
// fields: [
// { _id: 1, items: [1] },
// { _id: 2, items: [2, 3, 4] },
// { _id: 3, items: [5] }
// ]
// }
db.collection.update(
{},
[{ $set: {
fields: { $map: {
input: "$fields",
as: "x",
in: {
_id: "$$x._id",
items: { $map: {
input: "$$x.items",
as: "y",
in: { item: "$$y", key: 0 }
}}
}
}}
}}],
{ multi: true }
)
// {
// title: "record 1",
// fields: [
// { _id: 1, items: [ { item: 1, key: 0 } ] },
// { _id: 2, items: [ { item: 2, key: 0 }, { item: 3, key: 0 }, { item: 4, key: 0 } ] },
// { _id: 3, items: [ { item: 5, key: 0 } ] }
// ]
// }
-
Первая часть {}
- это запрос на совпадение, который фильтрует, какие документы обновлять (в данном случае все документы).
-
Вторая часть [{ $set: { fields: { $map: {... } } }]
представляет собой конвейер агрегации обновлений (обратите внимание на квадратные скобки, обозначающие использование конвейера агрегации):
-
$set
- новый оператор агрегирования, который в этом случае заменяет значение поля. - Затем (если мы пройдем шаблон), мы просто
$map
ping пропустим два вложенных массива. - Более конкретно, второе преобразование карты заменяет
[1, 2]
на [{ item: 1, key: 0 }, { item: 2, key: 0 }]
.
- Не забудьте
{ multi: true }
, иначе будет обновлен только первый соответствующий документ.