これは実際にはhttp://jira.mongodb.org/browse/SERVER-1243での長期にわたる問題に関連しています。実際には、複数の配列の一致がサポートされる「すべてのケース」をサポートする明確な構文に対する多くの課題があります。見つかりました。実際には、一括操作など、この問題の解決策を「支援」する方法がすでに導入されています。この元の投稿の後に実装さされています。
単一のupdateステートメントで一致する配列要素を1つ以上更新することはまだ不可能です。そのため、「マルチ」更新を使用しても、更新できるのは、その単一のドキュメントごとに配列内の1つの計算された要素だけです。ステートメント。
現時点で可能な最良の解決策は、一致するすべてのドキュメントを見つけてループし、一括更新を処理することです。これにより、単一のリクエストで単一のリクエストで多くの操作を送信できるようになります。オプションでを使用.aggregate()
して、検索結果で返される配列コンテンツを更新選択の条件に一致するものだけに減らすことができます。
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$setDifference": [
{ "$map": {
"input": "$events",
"as": "event",
"in": {
"$cond": [
{ "$eq": [ "$$event.handled", 1 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
]).forEach(function(doc) {
doc.events.forEach(function(event) {
bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
"$set": { "events.$.handled": 0 }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
}
});
});
if ( count % 1000 != 0 )
bulk.execute();
.aggregate()
アレイの「ユニーク」識別子または各要素に対するすべてのコンテンツがある場合が動作する部分は、「ユニーク」要素自体を形成します。これは、配列の一致を処理するために使用される操作から返された値$setDifference
をフィルタリングするために使用される「セット」演算子が原因です。false
$map
配列のコンテンツに一意の要素がない場合は、次の方法で別の方法を試すことができます$redact
。
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
制限があるのは、「処理された」が実際に他のドキュメントレベルに存在することを意図したフィールドであった場合、予期しない結果が得られる可能性が高いですが、そのフィールドが1つのドキュメント位置にのみ表示され、等価一致である場合は問題ありません。
執筆時点での将来のリリース(3.1 MongoDB以降)では、$filter
操作がより簡単になります。
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$filter": {
"input": "$events",
"as": "event",
"cond": { "$eq": [ "$$event.handled", 1 ] }
}
}
}}
])
また、サポート.aggregate()
するすべてのリリースでは、で次のアプローチを使用できます$unwind
が、その演算子を使用すると、パイプラインでの配列の拡張により、最も効率の悪いアプローチになります。
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"events": { "$push": "$events" }
}}
])
MongoDBバージョンが集約出力からの「カーソル」をサポートしているすべての場合、これは、アプローチを選択し、表示された同じコードブロックで結果を反復して、一括更新ステートメントを処理するだけの問題です。一括出力と集約出力からの「カーソル」は同じバージョン(MongoDB 2.6)で導入されているため、通常は連携して処理を行います。
以前のバージョンでも.find()
、カーソルを返すためにだけ使用し、.update()
反復のために配列要素が一致した回数だけステートメントの実行を除外するのがおそらく最善です。
db.collection.find({ "events.handled": 1 }).forEach(function(doc){
doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
});
});
「マルチ」更新を行うことが絶対的に決まっている場合、または一致する各ドキュメントの複数の更新を処理するよりも最終的に効率的であると判断した場合は、可能な最大配列一致数を常に決定し、その数の「マルチ」更新を実行する基本的に、更新するドキュメントがなくなるまで。
MongoDB 2.4および2.2バージョンの有効なアプローチも.aggregate()
、この値を見つけるために使用できます。
var result = db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"count": { "$max": "$count" }
}}
]);
var max = result.result[0].count;
while ( max-- ) {
db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}
いずれにせよ、アップデート内で実行したくないことがいくつかあります。
「ワンショット」で配列を更新しないでください。コード内の配列コンテンツ全体を更新してから$set
、各ドキュメントの配列全体を更新する方が効率的であると思われる場合。これは処理が高速に見えるかもしれませんが、配列の内容が読み取られてから更新された後、配列の内容が変更されていないという保証はありません。けれどもは$set
それだけで、それが正しいデータである「考えて」、したがって、読み取りと書き込みの間に発生するすべての変更を上書きする可能性があるものと配列を更新します、まだアトミック演算子です。
更新するインデックス値を計算しないでください。「ワンショット」アプローチと同様に、その位置0
と位置を計算するだけで2
(など)、更新する要素があり、次のような最終的なステートメントでこれらをコーディングします。
{ "$set": {
"events.0.handled": 0,
"events.2.handled": 0
}}
ここでも問題は、ドキュメントが読み取られたときに見つかったインデックス値が、更新時に配列内の同じインデックス値であるという「推定」です。順序を変更する方法で配列に新しい項目が追加されると、それらの位置は無効になり、実際には間違った項目が更新されます。
したがって、一致する複数の配列要素を単一の更新ステートメントで処理できるようにするために適切な構文が決定されるまで、基本的なアプローチは、一致する各配列要素を個別のステートメント(理想的にはBulk)で更新するか、基本的に最大の配列要素を計算することです変更された結果が返されなくなるまで、更新または更新を継続します。とにかく、ステートメントごとに1つの要素しか更新していない場合でも、一致した配列要素の位置の$
更新を「常に」処理する必要があります。
一括操作は、実際には「複数の操作」であることが判明した操作を処理するための「一般化された」ソリューションであり、同じ値で複数の配列要素を単に更新するだけではなく、これより多くのアプリケーションがあるため、もちろん実装されています。すでに、そしてそれは現在この問題を解決するための最良のアプローチです。