2016-08-03 12 views
10

Pobieram niektóre rekordy z MongoDB za pomocą Mongoose, importując je do innego systemu, a następnie chciałbym ustawić status (atrybut dokumentu) dla wszystkich tych dokumentów na processed.Jakie jest właściwe podejście do aktualizacji wielu rekordów w MongoDB za pomocą Mongoose

mogę znaleźć tego rozwiązania: Update multiple documents by id set. Mongoose

Zastanawiałem się, czy to jest właściwe podejście do budowania kryterium składają się wszystkie identyfikatory dokumentu, a następnie wykonać aktualizację. Proszę również wziąć pod uwagę fakt, że będzie to wiele dokumentów.

(Jaki jest limit zapytania aktualizacji nie można go nigdzie znaleźć oficjalnej dokumentacji. http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html)

Odpowiedz

12

Podejście budowania kryterium składają się wszystkie identyfikatory dokumentu, a następnie wykonywania aktualizacji jest związany powodować potencjalne problemy. Kiedy przeglądasz listę dokumentów wysyłających operację aktualizacji z każdym dokumentem, w Mongoose masz ryzyko wysadzenia serwera, szczególnie gdy masz do czynienia z dużym zbiorem danych, ponieważ nie czekasz na zakończenie asynchronicznego połączenia przed przejściem do następnego iteracja. Będziesz zasadniczo budować "stos" nierozwiązanych operacji, dopóki nie spowoduje to problemu - Stackoverflow.

Weźmy na przykład zakładając miałeś tablicę identyfikatorów dokumentu, który chciał, aby zaktualizować dokument pasujący na polu statusu:

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

następnie do bardzo małych zbiorów danych można użyć metody forEach() na tablica do iteracji i aktualizacji kolekcji:

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

Powyższe jest w porządku dla małych zestawów danych. Jednak staje się to problemem, gdy masz do czynienia z tysiącami lub milionami dokumentów do zaktualizowania, ponieważ będziesz wykonywać powtarzające się wywołania serwerów asynchronicznego kodu w pętli.

Alternatywą byłoby użyć czegoś podobnego ASYNC za eachLimit i iteracyjne nad tablicy wykonującego operację aktualizacji MongoDB dla każdej pozycji podczas wykonywania nie więcej niż x równoległych aktualizacjach tym samym czasie.


Najlepszym rozwiązaniem byłoby wykorzystanie zbiorczego API dla tego, który jest niezwykle skuteczny w przetwarzaniu aktualizacji luzem. Różnica w wydajności i wywoływanie operacji aktualizacji na każdym z wielu dokumentów polega na tym, że zamiast wysyłać żądania aktualizacji do serwera przy każdej iteracji, zbiorczy interfejs API wysyła żądania raz na każde 1000 żądań (w pakiecie).

Dla wersji Mongoose >=4.3.0 które wspierają MongoDB Server 3.2.x, można użyć bulkWrite() aktualizacji.Poniższy przykład pokazuje, jak można iść na ten temat:

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

Dla wersji Mongoose ~3.8.8, ~3.8.22, 4.x które wspierają MongoDB Server >=2.6.x, można użyć luzem API następująco

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

To jest dokładnie to, czego szukałem. Wielkie dzięki! –

+0

Czy mógłbyś powiedzieć mi, jak różni się "bulkWrite" od 'insertMany'? –

+0

Albo w jaki sposób 'collection.insert' różni się od' collection.bulkWrite'? Nie mogę znaleźć żadnej oficjalnej dokumentacji na temat tych rzeczy :(Reference: http://www.unknownerror.org/opensource/Automattic/mongoose/q/stackoverflow/16726330/mongoose-mongodb-batch-insert –

0

Możesz użyć opcji {multi: true} w kwerendzie aktualizacji aktualizacji zbiorczej.

Przykład:

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

Powyższy kod w mangusta odpowiada poniższym kodzie w MongoDB:

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