De aanpak om een criterium op te bouwen dat bestaat uit alle document-ID's en vervolgens de update uit te voeren, zal ongetwijfeld mogelijke problemen veroorzaken. Wanneer u een lijst met documenten herhaalt die een updatebewerking met elk document verzenden, loopt u in Mongoose het risico uw server op te blazen, vooral wanneer u te maken hebt met een grote dataset, omdat u niet wacht tot een asynchrone aanroep is voltooid voordat u doorgaat naar de volgende iteratie. U bouwt in wezen een "stapel" van onopgeloste bewerkingen totdat dit een probleem veroorzaakt - Stackoverflow.
Stel bijvoorbeeld dat u een reeks document-ID's had die u het overeenkomende document in het statusveld wilde bijwerken:
const processedIds = [
"57a0a96bd1c6ef24376477cd",
"57a052242acf5a06d4996537",
"57a052242acf5a06d4996538"
];
waar u de updateMany()
kunt gebruiken
methode
Model.updateMany(
{ _id: { $in: processedIds } },
{ $set: { status: "processed" } },
callback
);
of voor echt kleine datasets kunt u de forEach()
methode op de array om deze te herhalen en uw verzameling bij te werken:
processedIds.forEach(function(id)){
Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});
Het bovenstaande is oké voor kleine datasets. Dit wordt echter een probleem wanneer u geconfronteerd wordt met duizenden of miljoenen documenten die moeten worden bijgewerkt, aangezien u herhaaldelijk serveraanroepen van asynchrone code binnen de lus zult doen.
Om dit te verhelpen, gebruikt u iets als async's eachLimit
en herhaal de array door een MongoDB-updatebewerking uit te voeren voor elk item terwijl u nooit meer dan x parallelle updates tegelijkertijd uitvoert.
De beste aanpak zou zijn om hiervoor de bulk-API te gebruiken, die uiterst efficiënt is in het verwerken van updates in bulk. Het verschil in prestatie versus het aanroepen van de update-bewerking op elk van de vele documenten is dat in plaats van de update-verzoeken bij elke iteratie naar de server te sturen, de bulk-API de verzoeken eens in elke 1000 verzoeken (batch) verzendt.
Voor Mongoose-versies >=4.3.0
die MongoDB Server 3.2.x
ondersteunen , kunt u bulkWrite()
voor updates. Het volgende voorbeeld laat zien hoe u dit kunt doen:
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);
}
Voor Mongoose-versies ~3.8.8
, ~3.8.22
, 4.x
die MongoDB Server >=2.6.x
ondersteunen , kunt u de Bulk API als volgt gebruiken
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
});
}