Подходът за изграждане на критерий, състоящ се от всички идентификационни номера на документи и след това извършване на актуализация, неизбежно ще причини потенциални проблеми. Когато итерирате списък с документи, изпращайки операция за актуализиране с всеки документ, в Mongoose рискувате да взривите сървъра си, особено когато работите с голям набор от данни, защото не чакате асинхронно извикване да завърши, преди да преминете към следващия повторение. По същество ще изграждате „стек“ от неразрешени операции, докато това не предизвика проблем - Stackoverflow.
Да вземем за пример, да предположим, че имате масив от идентификационни номера на документи, които искате да актуализирате за съответстващия документ в полето за състояние:
const processedIds = [
"57a0a96bd1c6ef24376477cd",
"57a052242acf5a06d4996537",
"57a052242acf5a06d4996538"
];
където можете да използвате updateMany()
метод
Model.updateMany(
{ _id: { $in: processedIds } },
{ $set: { status: "processed" } },
callback
);
или алтернативно за наистина малки набори от данни можете да използвате forEach()
метод на масива, за да го итерирате и актуализирате вашата колекция:
processedIds.forEach(function(id)){
Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});
Горното е подходящо за малки набори от данни. Това обаче се превръща в проблем, когато се сблъскате с хиляди или милиони документи за актуализиране, тъй като ще извършвате повтарящи се сървърни извиквания на асинхронен код в рамките на цикъла.
За да преодолеете това, използвайте нещо като eachLimit<на async /код>
и итерирайте масива, изпълнявайки операция за актуализиране на MongoDB за всеки елемент, като никога не извършвате повече от x паралелни актуализирания по едно и също време.
Най-добрият подход би бил да използвате груповия API за това, който е изключително ефективен при групова обработка на актуализации. Разликата в производителността спрямо извикването на операцията за актуализиране на всеки един от многото документи е, че вместо да изпраща заявките за актуализиране към сървъра с всяка итерация, масовият API изпраща заявките веднъж на всеки 1000 заявки (пакетно).
За версии на Mongoose >=4.3.0
които поддържат MongoDB сървър 3.2.x
, можете да използвате bulkWrite()код>
за актуализации. Следният пример показва как можете да направите това:
const bulkUpdateCallback = function(err, r){
console.log(r.matchedCount);
console.log(r.modifiedCount);
}
// Initialize the bulk operations array
const bulkUpdateOps = [], counter = 0;
processedIds.forEach(function (id) {
bulkUpdateOps.push({
updateOne: {
filter: { _id: id },
update: { $set: { status: "processed" } }
}
});
counter++;
if (counter % 500 == 0) {
// Get the underlying collection via the Node.js driver collection object
Model.collection.bulkWrite(bulkUpdateOps, { ordered: true, w: 1 }, bulkUpdateCallback);
bulkUpdateOps = []; // re-initialize
}
})
// Flush any remaining bulk ops
if (counter % 500 != 0) {
Model.collection.bulkWrite(bulkOps, { ordered: true, w: 1 }, bulkUpdateCallback);
}
За версии на Mongoose ~3.8.8
, ~3.8.22
, 4.x
които поддържат MongoDB сървър >=2.6.x
, можете да използвате Bulk API както следва
var bulk = Model.collection.initializeOrderedBulkOp(),
counter = 0;
processedIds.forEach(function(id) {
bulk.find({ "_id": id }).updateOne({
"$set": { "status": "processed" }
});
counter++;
if (counter % 500 == 0) {
bulk.execute(function(err, r) {
// do something with the result
bulk = Model.collection.initializeOrderedBulkOp();
counter = 0;
});
}
});
// Catch any docs in the queue under or over the 500's
if (counter > 0) {
bulk.execute(function(err,result) {
// do something with the result here
});
}