すべてのドキュメントIDで構成される基準を構築してから更新を実行するというアプローチは、潜在的な問題を引き起こす可能性があります。各ドキュメントで更新操作を送信するドキュメントのリストを繰り返すと、Mongooseでは、非同期呼び出しが完了するのを待ってから次のデータセットに進むことがないため、特に大きなデータセットを処理するときにサーバーが爆発するリスクがあります。反復。これが問題を引き起こすまで、基本的に未解決の操作の「スタック」を構築します-Stackoverflow。
たとえば、ステータスフィールドで一致するドキュメントを更新するドキュメントIDの配列があるとします。
const processedIds = [
"57a0a96bd1c6ef24376477cd",
"57a052242acf5a06d4996537",
"57a052242acf5a06d4996538"
];
ここで、 updateMany()
を使用できます。
メソッド
Model.updateMany(
{ _id: { $in: processedIds } },
{ $set: { status: "processed" } },
callback
);
または、非常に小さなデータセットの場合は、 forEach()
配列を反復処理してコレクションを更新するメソッド:
processedIds.forEach(function(id)){
Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});
上記は小さなデータセットには問題ありません。ただし、ループ内で非同期コードのサーバー呼び出しを繰り返し行うため、更新するドキュメントが数千または数百万に達すると、これが問題になります。
これを克服するには、asyncの eachLimit<のようなものを使用します。 / code>
配列を反復処理して、アイテムごとにMongoDB更新操作を実行しますが、同時にxを超える並列更新を実行することはありません。
最善のアプローチは、このためにバルクAPIを使用することです。これは、更新を一括で処理するのに非常に効率的です。パフォーマンスと多くのドキュメントのすべてで更新操作を呼び出すことの違いは、反復ごとに更新要求をサーバーに送信する代わりに、バルクAPIが1000要求ごとに1回要求を送信することです(バッチ処理)。
Mongooseバージョンの場合>=4.3.0
MongoDBサーバー3.2.x
をサポートします 、 bullkWrite()<を使用できます/ code>
更新のため。次の例は、これを実行する方法を示しています。
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);
}
マングースバージョンの場合〜3.8.8
、〜3.8.22
、 4.x
MongoDBサーバーをサポートする>=2.6.x
、次のようにBulkAPIを使用できます
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
});
}