2016-08-03 8 views
10

Ich ziehe einige MongoDB-Datensätze mit Mongoose, importieren sie in ein anderes System und dann möchte ich Status (Dokument Attribut) für alle diese Dokumente auf processed setzen.Was ist der richtige Ansatz zum Aktualisieren vieler Datensätze in MongoDB mit Mongoose

ich diese Lösung gefunden: Update multiple documents by id set. Mongoose

ich mich gefragt, ob das der richtige Ansatz ist, ein Kriterium für den Aufbau aller Dokument-IDs aus und dann das Update durchzuführen. Bitte beachten Sie auch, dass es viele Dokumente geben wird.

(Was die Grenze der Update-Abfrage ist Könnte es nicht überall Offizielle Dokumentation. http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html)

Antwort

12

Der Ansatz, ein Kriterium aller Dokument-IDs aus dem Aufbau und die Aktualisierung durchgeführt worden ist, gebunden an Mögliche Probleme verursachen. Wenn Sie eine Liste von Dokumenten durchlaufen, die mit jedem Dokument eine Aktualisierungsoperation senden, riskieren Sie in Mongoose den Server zu sprengen, besonders wenn Sie mit einem großen Dataset arbeiten, weil Sie nicht auf einen asynchronen Aufruf warten, bevor Sie zum nächsten gehen Iteration. Sie werden im Wesentlichen einen "Stapel" ungelöster Operationen erstellen, bis dies ein Problem verursacht - Stackoverflow.

Nehmen Sie zum Beispiel, angenommen, Sie eine Reihe von Dokumenten-IDs hatte, dass Sie das passende Dokument auf dem Statusfeld aktualisieren wollte:

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

dann für wirklich kleine Datensätze Sie die Methode forEach() nutzen könnten auf das Array zu iterieren und aktualisieren Sie Ihre Sammlung:

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

Das obige ist in Ordnung für kleine Datensätze. Dies wird jedoch zu einem Problem, wenn Sie mit Tausenden oder Millionen von zu aktualisierenden Dokumenten konfrontiert werden, da Sie wiederholte Serveraufrufe von asynchronem Code innerhalb der Schleife ausführen.

Eine Alternative wäre so etwas wie eachLimit des async zu nutzen und über das Array iterieren für jedes Element eines MongoDB Aktualisierungsvorgang ausführen, während nie mehr als x parallel Updates zur gleichen Zeit durchführen.

Der beste Ansatz wäre, die Bulk-API dafür zu verwenden, die bei der Verarbeitung von Bulk-Aktualisierungen äußerst effizient ist. Der Unterschied in der Leistung im Vergleich zum Aufrufen der Aktualisierungsoperation für jedes der vielen Dokumente besteht darin, dass die Bulk-API die Anforderungen nicht einmal pro 1000 Anforderungen (stapelweise) sendet, sondern die Aktualisierungsanforderungen mit jeder Iteration an den Server sendet.

Für Mongoose Versionen >=4.3.0 die 3.2.x MongoDB Server unterstützt, können Sie bulkWrite() für Updates verwenden.Das folgende Beispiel zeigt, wie Sie vorgehen können:

var bulkUpdateCallback = function(err, r){ 
    console.log(r.matchedCount); 
    console.log(r.modifiedCount); 
} 
// Initialise the bulk operations array 
var 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 native node.js driver collection object 
     Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback); 
     bulkUpdateOps = []; // re-initialize 
    } 
}) 

if (counter % 500 != 0) { Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback); } 

Für Mongoose Versionen ~3.8.8, ~3.8.22, 4.x die Unterstützung MongoDB Server >=2.6.x, könnten Sie die Bulk-API verwenden, wie

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

Das ist genau das, was ich suchte. Danke vielmals! –

+0

Würde es Ihnen etwas ausmachen, mir zu sagen, wie sich "bulkWrite" von "insertMany" unterscheidet? –

+0

Oder wie unterscheidet sich 'collection.insert' von' collection.bulkWrite'? Ich kann anscheinend keine offizielle Dokumentation zu diesen Sachen finden :(Referenz: http://www.unknownerror.org/opensource/Automatic/mongoose/q/stackoverflow/16726330/mongoose-mongodb-batch-insert –

0

folgt Sie können {multi: true} Option in Ihrer Update-Abfrage für Massenupdate verwenden.

Beispiel:

employees.update({ _id: { $gt: 3 } },{$inc: { sortOrder: -1 }},{'multi':true}); 

Der obige Code in Mungo entspricht dem folgenden Code in mongodb:

db.employees.updateMany({ _id: { $gt: 3 } },{$inc: { sortOrder: -1 }});