前置き
あまり設計に慣れておらず,ややこしい(?)入れ子構造でデータを作ってしまい,データ修正に困ったので,覚え書き.
DB 構造
[
{
scores: [
{
player_id: "user1"
point: 10,
events: [
{
player_id: "user1"
id_in_another_collection: "foobar",
img_id: "imageimage"
}
},
{
player_id: "user2"
point: 30,
events: [
{
player_id: "user2"
id_in_another_collection: "barfoo",
img_id: "imageimage2"
}
{
player_id: "user2"
id_in_another_collection: "barfoobar",
img_id: "imageimage3"
}
]
}
],
other_data: "others...",
},
{...},
...
]
player_id
が scores.$.event.$
で被っていることに気付いて,あとで $unset
しようとして,ハマったお話です.
問題のややこしさ
見ての通り,最深部の player_id
にたどり着くまでに
(all_records ->) record -> scores -> score -> events -> event -> player_id
,すなわち,(array ->) object -> array -> object -> array -> object
というややこしい手順を踏まなければならない...
解決策
db.scores.updateMany(
{"scores.events.player_id": { $exists: true, $not: {$size: 0} }},
{$unset: {"scores.$[].events.$[e].player_id": ""}},
{"arrayFilters": [{"e.player_id": {$ne: null}}]})
第一引数
player_id
が空でないレコードを抜き出している.後に arrayFilters
を適用するので不要かも?
第二引数
$unset
の指定.player_id
を unset
します,という意.
"scores.$[].events.$[e].player_id"
の $[e]
は identifier と呼ばれているもので,公式ドキュメント1 によれば,
The filtered positional operator $[] identifies the array elements that match the arrayFilters conditions for an update operation, e.g. db.collection.updateMany() and db.collection.findAndModify().
すなわち,指定したフィルタにマッチする配列のキーを与えてくれるそうです.
$[]
は配列の全ての要素,という意味2.
フィルタについては第三引数を参照.
第三引数
第二引数で取り上げたフィルタを指定.
e
は,第二引数において $[e]
と表記した配列キーが指定しているオブジェクトに対応する(event.$[e] = e
).
"e.player_id" : {$ne: null}
は,配列 event
の任意の要素 e
について,e.player_id
が null
でなければフィルタに引っかかる,という意味である.
このときに引っかかった $[e]
(実体は 0, 1 などの配列キー)を第二引数で用いて $unset
してくれる.