HOWTO · MongoDB

MongoDBでネストされたフィールドをプロジェクションする

MongoDBでネストされたフィールドをプロジェクションする方法を解説します。ドット表記とfind()による選択、$projectによる集約結果の整形、$unsetによるフィールド削除を説明します。

このページの内容

MongoDB のドキュメントには、ユーザーレコード内の address オブジェクトのような埋め込みドキュメントを含められます。プロジェクションは、クエリが返すフィールドを指定します。たとえば address.city を選択すると、ネストされた構造を保ったまま、その値が返されます。単純なクエリ結果には find() プロジェクションを使い、集約パイプラインでドキュメントの形を整えるには $project を使います。パイプラインの結果から特定のフィールドを削除するには $unset を使います。

MongoDB におけるネストされたフィールドのプロジェクション

ドキュメントに name と address のフィールドがあり、address に street、city、country が含まれているとします。ネストされたパスは address.city のようにドット表記で指定します。プロジェクションが変えるのは返されるドキュメントであり、保存済みの元のドキュメントではありません。以下の例では、同じ構造のドキュメントを使います。find() のプロジェクションでは、明示的に除外しない限り _id はデフォルトで含まれます。

ドット表記と find() を使ってネストされたフィールドを選択する

ネストされた address から city だけを返すには、そのドット区切りのパスを含めます。次の MongoDB シェル式は構文の例であり、設定された実行環境では実行されません。

db.users.find({}, { "address.city": 1, "_id": 0 })

返される形は { "address": { "city": "New York" } } です。各結果に識別子も残す場合は _id: 0 を省略します。他のフィールドを残したままネストされたパスを除外するには、{ "address.country": 0 } のように 0 を指定します。

通常、包含プロジェクションでは、含めるフィールドと除外するフィールドを混在させられません。例外は _id で、他のパスを含めながら _id を除外できます。それ以外のフィールドは、包含プロジェクションか除外プロジェクションのどちらかを選びます。

次の例を実行してください。

このセクションのコードと表示されるラベルや出力は英語のままです。

document = {"name": "Ava", "address": {"city": "New York", "country": "USA"}}
result = {"address": {"city": document["address"]["city"]}}
print(result)
{'address': {'city': 'New York'}}

この短い Python の例は、返されるネスト構造を示しています。MongoDB には接続しません。上記の MongoDB クエリでは、対応するドット区切りのパスを使っています。コード内のラベルと出力文字列は、バイト単位で同一の例を保つため英語のままです。

$project 集約ステージでネストされたフィールドの形を整える

集約パイプラインでは、$project を使ってネストされたパスを含めたり、入力元のパスを出力フィールドに対応付けたりできます。たとえば { $project: { _id: 0, city: "$address.city", name: 1 } } は、city をトップレベルに配置し、name とともに返します。一方、{ $project: { "address.city": 1, _id: 0 } } のようにパスをそのまま含めると、address.city のネスト構造が保たれます。このように、パイプラインで出力の形を明示的に整える必要がある場合に $project が役立ちます。式を使って出力値を計算することもできます。その場合は、各式が入力ドキュメントに存在するフィールドを参照していることを確認してください。

$unset 集約ステージでネストされたフィールドを除外する

集約の $unset ステージを使うと、他のフィールドを残したまま1つ以上のパスを削除できます。例: { $unset: ["address.country"] }。ドット区切りのパスを指定すると、そのネストされたフィールドがパイプラインの結果から取り除かれます。$unset が変えるのは、パイプラインを通過するフィールドであり、ドキュメントを絞り込むわけではありません。ドキュメントを絞り込むには、クエリ条件または $match ステージを使います。このステージは集約機能であり、同じ名前を持つ更新演算子とは別のものです。

クエリのプロジェクションと集約の使い分け

クエリ結果に含めるフィールドや除外するフィールドを指定するだけなら、ドット表記と find() を使います。集約でパス名の変更、値の計算、または意図した出力形状の定義が必要なら、$project を選びます。パイプラインで大部分のフィールドを残し、いくつかだけを削除する場合は $unset を選びます。$addFields はドキュメントの他の部分を残しながらフィールドを追加または置換するため、プロジェクションによる選択の代わりにはなりません。いずれの方法でも、プロジェクションやフィールドの整形が影響するのは結果であり、保存済みのドキュメントではありません。