sql >> Databasteknik >  >> NoSQL >> MongoDB

Vad är rätt tillvägagångssätt för att uppdatera många poster i MongoDB med Mongoose

Tillvägagångssättet att bygga upp ett kriterium som består av alla dokument-ID och sedan utföra uppdateringen kommer definitivt att orsaka potentiella problem. När du upprepar en lista med dokument som skickar en uppdateringsoperation med varje dokument, riskerar du i Mongoose att spränga din server, särskilt när du har att göra med en stor datamängd eftersom du inte väntar på att ett asynkront samtal ska slutföras innan du går vidare till nästa iteration. Du kommer i huvudsak att bygga en "stack" av olösta operationer tills detta orsakar ett problem - Stackoverflow.

Anta till exempel att du hade en uppsättning dokument-ID som du ville uppdatera det matchande dokumentet i statusfältet:

const processedIds = [
  "57a0a96bd1c6ef24376477cd",
  "57a052242acf5a06d4996537",
  "57a052242acf5a06d4996538"
];

där du kan använda updateMany() metod

Model.updateMany(
  { _id: { $in: processedIds } }, 
  { $set: { status: "processed" } }, 
  callback
);

eller för riktigt små datauppsättningar kan du använda forEach() metod på arrayen för att iterera den och uppdatera din samling:

processedIds.forEach(function(id)){
  Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});

Ovanstående är okej för små datamängder. Detta blir dock ett problem när du ställs inför tusentals eller miljontals dokument att uppdatera eftersom du kommer att göra upprepade serveranrop av asynkron kod inom loopen.

För att övervinna detta använd något som asyncs eachLimit och iterera över arrayen och utför en MongoDB-uppdateringsoperation för varje objekt samtidigt som du aldrig utför mer än x parallella uppdateringar samtidigt.

Det bästa tillvägagångssättet skulle vara att använda bulk-API:et för detta, vilket är extremt effektivt för att behandla uppdateringar i bulk. Skillnaden i prestanda jämfört med att anropa uppdateringsoperationen på vart och ett av de många dokumenten är att istället för att skicka uppdateringsförfrågningarna till servern med varje iteration, skickar bulk-API:et förfrågningarna en gång på 1000 förfrågningar (batchade).

För Mongoose-versioner >=4.3.0 som stöder MongoDB Server 3.2.x , du kan använda bulkWrite() för uppdateringar. Följande exempel visar hur du kan gå tillväga:

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

För Mongoose-versioner ~3.8.8 , ~3.8.22 , 4.x som stöder MongoDB Server >=2.6.x , kan du använda Bulk API enligt följande

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. Mongoose {strict:throw} kastar inte fel

  2. Är det möjligt att använda MongoDB som en inbäddad databas?

  3. NodeJS - anslut säkert till extern redis-server

  4. Hur hittar man det matchade rekordet i mongodb?