sql >> Database >  >> NoSQL >> MongoDB

Wat is de juiste aanpak om veel records in MongoDB bij te werken met Mongoose

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
    });
}


  1. Hoe ReadConcernMajority inschakelen in MongoDB 3.6.3?

  2. Zijn er redenen waarom ik ObjectId's wel/niet zou moeten gebruiken in mijn RESTful url's

  3. De functie db.system.js aanroepen in $where

  4. MongoDB:Upserting en Sub-documenten