13

mongoDB に深くネストされたドキュメントがあり、個々のサブオブジェクトを取得したいと考えています。

例:

{
   "schoolName": "Cool School",
   "principal": "Joe Banks",
   "rooms": [
      {
         "number": 100
         "teacher": "Alvin Melvin"
         "students": [
            {
               "name": "Bort"
               "currentGrade": "A"
            },
            // ... many more students
         ]
      },
      // ... many more rooms
   ]
}

最近 Mongo が更新され、 $elemMatch プロジェクションを使用して 1 レベルの深さのサブオブジェクトを取得できるようになりました。

var projection = { _id: 0, rooms: { $elemMatch: { number: 100 } } };
db.schools.find({"schoolName": "Cool School"}, projection);
// returns { "rooms": [ /* array containing only the matching room */ ]  }

しかし、同じ方法で学生 (2 レベルの深さ) をフェッチしようとすると、エラーが発生します。

var projection = { _id: 0, "rooms.students": { $elemMatch: { name: "Bort" } } };
db.schools.find({"schoolName": "Cool School"}, projection);
// "$err": "Cannot use $elemMatch projection on a nested field (currently unsupported).", "code": 16344

mongoDB ドキュメントで任意の深いサブオブジェクトを取得する方法はありますか?

私はMongo 2.2.1を使用しています

4

2 に答える 2

3

私は最近同様の質問をしましたが、適切に一般的な回答を提供できます (「深くネストされたドキュメント クエリで MongoDB の位置演算子 $ を使用する」を参照してください) 。

このソリューションは Mongo 2.6 以降でのみサポートされていますが、それ以降は集計フレームワークの $redact 関数を使用できます。

生徒の Bort だけを返すクエリの例を次に示します。

db.users.aggregate({

    $match: { schoolName: 'Cool School' }

}, {

    $project: {

        _id: 0,
        'schoolName': 1,
        'rooms.number': 1,
        'rooms.students': 1

    }

}, {

    $redact: {
        $cond: {
            "if": {
                $or: [{
                    $gte: ['$schoolName', '']
                }, {
                    $eq: ['$number', 100]
                }]
            },
            "then": "$$DESCEND",
            "else": {
                $cond: {
                    "if": {
                        $eq: ['$name', 'Bort']
                    },
                    "then": "$$KEEP",
                    "else": "$$PRUNE"
                }
            }
        }
    }

});

$redact を使用して、一致したドキュメントで再帰的にサブドキュメントを一致またはプルーニングすることにより、サブクエリを作成できます。

ここで $redact について読んで何が起こっているのかを理解することができますが、私が特定した設計パターンには次の要件があります。

  • 編集条件はサブドキュメント レベルで適用されるため、各レベルで一意のフィールドが必要です。
  • 配列インデックスではなくデータフィールドでのみ機能するため、ネストされたドキュメントの返された位置を知りたい場合(たとえば、更新する場合)、それを含めてドキュメントに維持する必要があります
  • $redact の$orステートメントの各部分は、特定のレベルで必要なドキュメントと一致する必要があります
  • したがって、 $orステートメントの各部分には、そのレベルのドキュメントの一意のフィールドとの一致を含める必要があります。たとえば、$eq: ['$number', 100]100 番の部屋に一致します。
  • レベルでクエリを指定していない場合でも、一意のフィールドを含める必要があります。たとえば、それが文字列の場合、それを一致させることができます$gte: ['$uniqueField': '']
  • 最後のドキュメント レベルは2 番目のif 式に入るため、そのドキュメントのすべてが保持されます。
于 2014-11-02T20:42:41.257 に答える
0

現時点では mongodb 2.2 が手元にないため、これをテストすることはできませんが、試してみましたか?

var projection = { _id: 0, rooms: { $elemMatch: { "students.name": "Bort" } } };
db.schools.find({"schoolName": "Cool School"}, projection);
于 2012-11-27T21:00:34.227 に答える