Если у вас действительно есть MongoDB 4.2 или выше, вы можете использовать синтаксис new , доступный для update .По сути, это означает добавление одного из действительных операторов конвейера агрегации: $addFields
, $set
(псевдоним $addFields
для упрощения чтения «обновлений»), $project
или $replaceRoot
, а затем фактические операторы агрегирования для выполнения манипуляции.В этом случае $sum
:
let writeResult = await db.collection("collection").updateMany(
{},
[{ "$set": { "totalWeight": { "$sum": "$packages.weight" } } }]
);
Это добавляет новые поля totalWeight
к каждому документу на основе значений, присутствующих во всем массиве каждого документа.
Основное преимуществоэто то, что это единственный запрос к серверу, который фактически выполняет ВСЕ обновления на сервере и не требует никакой информации из коллекции для отправки клиенту для повторения.
Если выиметь более раннюю версию (я предлагаю вам не использовать ничего раньше, чем 3.4, но даже 3.2 здесь бы пригодился), тогда вы можете использовать bulkWrite()
в цикле:
async function updateCollection() {
let cursor = db.collection("collection").aggregate([
{ "$project": {
"totalWeight": { "$sum": "$packages.weight" }
}}
]);
let batch = [];
while ( await cursor.hasNext() ) {
let { _id, totalWeight } = await cursor.next();
batch.push({
"updateOne": {
"filter": { _id },
"update": { "$set": { totalWeight } }
}
});
if ( batch.length > 1000 ) {
await db.collection("collection").bulkWrite(batch);
batch = [];
})
}
if ( batch.length != 0 ) {
await db.collection("collection").bulkWrite(batch);
batch = [];
}
}
И это сделало бы то же самое, но, конечно, на самом деле требуется некоторое взаимодействие с сервером при чтении и записи результата обратно.Хотя при использовании bulkWrite()
вы отправляете записи назад только в пакетах , а не на один документ коллекции.
Если у вас есть более старая MongoDB, то Обновите поле MongoDB, используя значениедругого поля содержит некоторые ссылки на те же методы в итерации цикла , которые также могут применяться.Но я действительно рекомендую, чтобы у не было более старых версий MongoDB, чем те, которые упомянуты в ответе здесь.
NB Возможно, вы захотите добавитьнекоторые обработчики try/catch
в таком коде обновления также в случае ошибок.Или, с другой стороны, такие отдельные операции , вероятно, лучше выполнять в чем-то вроде оболочки MongoDB.
Обслуживание
Лучшее решение в целомоднако на самом деле должен поддерживать общее количество при каждом добавлении в массив.Например, это в основном то, что вам нужно при использовании $push
для нового элемента массива и $inc
для добавления к существующему итогу:
let package = {
"date": "2019-09-26T06:30:39.561Z",
"_id": "5d8c5b0f756838f78d5205d7",
"category": "chil",
"quantity": "177",
"description": "a valueablegoods",
"width": 15,
"itemweight": 123,
"length": 17,
"height": 21,
"dimension": 31.25903614457831,
"weight": 32.25903614457831
};
let writeResult = await db.collection('collection').udpdateOne(
{ "_id": myIdValue },
{
"$push": { "packages": package },
"$inc": { "totalWeight": package.weight
}
);
Таким образом, вы фактически проверяете, что total корректируется с каждым внесенным вами изменением, и, следовательно, не требует постоянной повторной обработки другого оператора для сохранения этого итога в документе.Подобные понятия применимы и к другим типам обновлений, кроме добавления нового элемента в массив.