それでコメントで質問しましたが、あなたは立ち去ったようですので、私が見た3つの考えられるケースに答えるだけだと思います。
そもそも、ネストされた配列内に表示される要素がのみであるかどうかはわかりません。 配列内の要素、または実際にはarrayToDelete
のみです それらの要素に存在するフィールド。だから基本的に私は抽象化する必要があります 少し、その場合を含めてください:
{
field: 'value',
field2: 'value',
scan: [
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
]
}
ケース1-フィールドが存在する内部配列要素を削除します
これは$pull
を使用します 配列要素を削除するので、演算子 全体的に。これは、最新のMongoDBで、次のようなステートメントを使用して行います。
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
)
これにより、一致するすべてのドキュメントが次のように変更されます:
{
"_id" : ObjectId("5ca1c36d9e31550a618011e2"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
]
]
}
そのため、そのフィールドを含むすべての要素が削除されます。
ケース2-一致したフィールドを内部要素から削除するだけです
ここで$unset
を使用します 。 「ハードインデックス」とは少し異なります。 あなたがやっていたバージョン:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[].$[].arrayToDelete": "" } }
)
これにより、一致するすべてのドキュメントが次のように変更されます。
{
"_id" : ObjectId("5ca1c4c49e31550a618011e3"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
],
[
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
}
],
[
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
]
]
}
したがって、すべてがまだそこにありますが、識別されたフィールドだけが各内部配列ドキュメントから削除されています。
ケース3-アレイ内の「すべて」を実際に削除したかったのです。
これは、実際には$set
を使用する単純なケースです。 以前にあったものをすべて消去します:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$set": { "scan": [] } }
)
結果がかなり期待できる場所:
{
"_id" : ObjectId("5ca1c5c59e31550a618011e4"),
"field" : "value",
"field2" : "value",
"scan" : [ ]
}
では、これらはすべて何をしているのでしょうか?
最初に表示されるのは、クエリ述語です。 。これは一般的に、一致していないこと、さらには「試行」していないことを確認することをお勧めします 更新しようとしているパターンのデータさえ含まれていないドキュメントで更新条件が満たされるようにするため。ネストされた配列は難しい せいぜい、そして実際的な場合は、あなたがしばしば「本当に意味する」のように、それらを避けるべきです。 実際には、あなたが「考える」ことを表す追加の属性を持つ単一の配列で表されます ネスティング 実際にやっています。
しかし、彼らが難しいという理由だけで 不可能を意味するものではありません 。 $elemMatch
を理解する必要があるだけです :
db.colelction.find(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
}}
)
これが基本的なfind()
たとえば、$elemMatch
に基づいて一致します 外部の条件 配列は別の$elemMatch
を使用します 内部の別の条件に一致させるため 配列。これは「表示される」 単一の述語になります。次のようなもの:
"scan.arrayToDelete": { "$exists": true }
ただ動作しません。どちらもしません:
"scan..arrayToDelete": { "$exists": true }
「ダブルドット」で..
それは基本的には有効ではないからです。
これがクエリ述語です。 処理する必要のある「ドキュメント」と一致しますが、残りは実際に*更新する部分を決定するために適用されます。
ケース1 $pull
するために 内部から 配列の場合、最初に外部のどの要素を識別できる必要があります。 配列には、更新するデータが含まれています。それが"scan.$[a]"
です。 位置フィルタリングされた$[<identifier>]
を使用して実行しています オペレーター。
その演算子は基本的に転置 一致したインデックス (したがって、多く それらのうち)別の述語への配列内 これは、update
の3番目のセクションで定義されています arrayFilters
を使用したスタイルコマンド セクション。このセクションでは、基本的に、名前付き識別子の観点から満たす必要のある条件を定義します。
この場合、「識別子」の名前はa
です。 、これはarrayFilters
で使用されるプレフィックスです エントリ:
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
実際の更新ステートメントとの関連で取得 パート:
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
次に、"a"
の観点から 外部の識別子である "scan"
から最初に内側に配列要素 の場合、元のクエリ述語の場合と同じ条件が適用されます。 ただし、「内」から 最初 $elemMatch
声明。したがって、これは基本的に、すでに「内部を見る」という観点からは「クエリ内のクエリ」と考えることができます。 各アウターのコンテンツ 要素。
同じように、$pull
「クエリ内のクエリ」のように機能します 配列の要素の観点からも、独自の引数が適用されるという点で。したがって、arrayToDelete
だけです 代わりに存在するフィールド:
// This would be wrong! and do nothing :(
{
"$pull": {
"scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
}
}
しかし、それはすべて$pull
に固有のものです 、およびその他のものにはさまざまなケースがあります:
ケース2 $unset
だけにしたい場所を確認します 名前付きフィールド。フィールドに名前を付けるだけなので、とても簡単なようですよね?以下は私たちが以前に知っていたものから明らかに正しくないので、正確にはそうではありません:
{ "$unset": { "scan.arrayToDelete": "" } } // Not right :(
そしてもちろん、すべての配列インデックスに注意するのは面倒です:
{ "$unset": {
"scan.0.0.arrayToDelete": "",
"scan.0.1.arrayToDelete": "",
"scan.0.2.arrayToDelete": "",
"scan.0.3.arrayToDelete": "", // My fingers are tired :-<
} }
これが、すべての$[]
の位置の理由です。 オペレーター。これはもう少し「ブルートフォース」です 位置フィルタリングされた$[<identifier>]
より その中で、別の述語と一致する代わりに arrayFilters
内で提供されます 、これが単純に行うことは、すべてに適用されることです。 その「インデックス」の配列コンテンツ内。これは基本的に、「すべてのインデックス」を実際に言う方法です。 恐ろしいのようにすべてを入力せずに 上記のケース。
したがって、すべての場合ではありません 、しかしそれは確かに$unset
に適しています それは非常に具体的なパスの命名を持っているからです もちろん、そのパスが配列のすべての要素と一致しない場合は問題になりません。
あなたはできた 引き続きarrayFilters
を使用します 位置フィルタリングされた$[<identifier>]
、しかしここではやり過ぎでしょう。さらに、他のアプローチを示すことは害にはなりません。
しかしもちろん、どのように理解する価値があるでしょう。 まさにそのステートメントは見えるので、:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[a].$[b].arrayToDelete": "" } },
{
"arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
{ "b.arrayToDelete": { "$exists": true } },
]
}
)
"b.arrayToDelete"
に注意してください 最初は期待したものではないかもしれませんが、"scan.$[a].$[b]
での位置付けを考えると b
からのように本当に意味があるはずです 要素名は、示されているように「ドット表記」を介して到達します。そして実際にはどちらの場合も。繰り返しになりますが、$unset
とにかく名前付きフィールドにのみ適用されるため、選択基準は実際には必要ありません。
そしてケース3 。 必要がないのであれば、それは非常に簡単です。 このコンテンツを削除した後、配列内に他のものを保持するため(つまり、$pull
これに一致するフィールドはのみでした そこにあるもの、または$unset
そのことについては)、それなら単に他のものをいじらないで、アレイをワイプする 。
これは、名前付きフィールドを持つドキュメントがのみであるかどうかを明確にするためのポイントに従って考慮する場合、重要な違いです。 ネストされた配列内の要素であり、実際、名前付きキーはのみでした。 文書に存在するもの。
$pull
を使用するという理由で ここに示されているように、そしてそれらの条件下であなたは得るでしょう:
{
"_id" : ObjectId("5ca321909e31550a618011e6"),
"field" : "value",
"field2" : "value",
"scan" : [
[ ],
[ ],
[ ]
]
}
または、$unset
を使用します :
{
"_id" : ObjectId("5ca322bc9e31550a618011e7"),
"field" : "value",
"field2" : "value",
"scan" : [
[{ }, { }, { }, { }],
[{ }, { }, { }, { }],
[{ }, { }, { }, { }]
]
}
どちらも明らかに望ましくありません。したがって、arrayToDelete
の場合は、当然のことです。 フィールドはのみでした そこにあったコンテンツ、そしてすべてを削除する最も論理的な方法 単に配列を空の配列に置き換えることです。または実際に$unset
ドキュメントプロパティ全体。
ただし、これらすべての「派手なもの」に注意してください。 ($set
を除く もちろん)少なくともMongoDB3.6が必要である必要があります この機能を使用するために利用できます。
それよりも古いバージョンのMongoDBをまだ実行している場合(そして、執筆時点では、公式サポートがこの日付からわずか5か月でなくなるため、実際にはそうすべきではありません)、複数の更新方法に関する他の既存の回答mongodbの配列要素は実際にはあなたのためです。