定義
$graphLookupバージョン 5.1 での変更。
再帰深度とクエリフィルターで検索を制限するオプションを使用して、コレクション内の再帰検索を実行します。
$graphLookup検索プロセスの概要を以下に示します。入力ドキュメントは、集計操作の
$graphLookupステージに入ります。$graphLookupfromパラメータで指定されたコレクションを検索対象とします(検索パラメータの完全なリストは以下を参照してください)。入力ドキュメントごとに、
startWithで指定された値から検索が始まります。$graphLookupstartWith値をfromコレクション内の他のドキュメントのconnectToFieldで指定されたフィールドと照合します。一致するドキュメントごとに、
$graphLookupはconnectFromFieldの値を取得し、fromコレクション内のすべてのドキュメントを確認し、一致するconnectToField値を探します。$graphLookupは、一致するたびにfromコレクション内の一致するドキュメントをasパラメータで指定された配列フィールドに追加します。この手順は、一致するドキュメントがなくなるまで、または操作が
maxDepthパラメータで指定された再帰深度に達するまで再帰的に続きます。$graphLookupにより、入力ドキュメントに配列フィールドが追加されます。すべての入力ドキュメントの検索が終わると、$graphLookupが結果を返します。
$graphLookupには、次のプロトタイプ形式があります。{ $graphLookup: { from: <collection>, startWith: <expression>, connectFromField: <string>, connectToField: <string>, as: <string>, maxDepth: <number>, depthField: <string>, restrictSearchWithMatch: <document> } } $graphLookup次のフィールドが含まれるドキュメントについて、フィールド説明from$graphLookupconnectFromFieldconnectToField操作で検索するターゲットコレクション。 を に再帰的に一致させます。fromコレクションは、操作で使用する他のコレクションと同じデータベース内になければなりません。MongoDB 5.1 以降では、
fromパラメータで指定されたコレクションをシャーディングできます。startWithconnectFromField$graphLookupconnectToFieldがコレクション内の他のドキュメントの と再帰的に一致するために使用する値を持つフィールド名。値が配列の場合、各要素は走査プロセスを通じて個別に追跡されます。connectToFieldconnectFromFieldパラメータで指定されたフィールドの値と一致する、他のドキュメント内のフィールド名。as各出力ドキュメントに追加される配列フィールドの名前。
$graphLookupドキュメントに到達するまでに ステージで走査されたドキュメントが含まれます。asフィールドに返されるドキュメントは、必ずしも任意の順序だとは限りません。maxDepth任意。 最大再帰深度を指定する負でない整数。
depthField任意。 検索パス内の走査済みドキュメントそれぞれに追加するフィールドの名前。このフィールドの値は当該ドキュメントの再帰深度で、
NumberLongと表されます。再帰深度の値はゼロから始まるため、最初の検索はゼロ深度となります。restrictSearchWithMatch
Considerations
シャーディングされたコレクション
MongoDB5.1 以降では、from ステージの パラメーターで$graphLookup シャーディングされたコレクション を指定できます。
シャーディングされたコレクションをターゲットにしている間は、トランザクション内で $graphLookup ステージを使用できません。
最大深度
maxDepthフィールドを0に設定することは、非再帰的な$graphLookup検索ステージと同じです。
メモリ
$graphLookup ステージは 100 メガバイトのメモリ制限内に収まる必要があります。aggregate() 操作に allowDiskUse: true が指定されている場合、$graphLookupステージではオプションは無視されます。aggregate() 操作に他のステージがある場合、allowDiskUse: true オプションはこれらの他のステージに対して有効になります。
詳細は「集計パイプラインの制限」を参照してください。
ソートされていない結果
$graphLookup ステージではソートされた結果は返されません。結果を並べ替えるには、$sortArray 演算子を使用します。
ビューと照合
複数のビューが関わる集計($lookup や $graphLookup など)が実行される場合、それらのビューには同じ照合が含まれる必要があります。
例
単一のコレクション内
employees という名前のコレクションには次のドキュメントが含まれています。
db.employees.insertMany( [ { _id: 1, name: "Dev" }, { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 3, name: "Ron", reportsTo: "Eliot" }, { _id: 4, name: "Andrew", reportsTo: "Eliot" }, { _id: 5, name: "Asya", reportsTo: "Ron" }, { _id: 6, name: "Dan", reportsTo: "Andrew" } ] )
次の$graphLookup 操作では、employees コレクションの reportsTo フィールドと name フィールドを再帰的に照合し、各人のレポート階層を返します。
db.employees.aggregate( [ { $graphLookup: { from: "employees", startWith: "$reportsTo", connectFromField: "reportsTo", connectToField: "name", as: "reportingHierarchy" } } ] )
[ { _id: 1, name: 'Dev', reportingHierarchy: [] }, { _id: 2, name: 'Eliot', reportsTo: 'Dev', reportingHierarchy: [ { _id: 1, name: 'Dev' } ] }, { _id: 3, name: 'Ron', reportsTo: 'Eliot', reportingHierarchy: [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' }, { _id: 1, name: 'Dev' } ] }, { _id: 4, name: 'Andrew', reportsTo: 'Eliot', reportingHierarchy: [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' }, { _id: 1, name: 'Dev' } ] }, { _id: 5, name: 'Asya', reportsTo: 'Ron', reportingHierarchy: [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' }, { _id: 3, name: 'Ron', reportsTo: 'Eliot' }, { _id: 1, name: 'Dev' } ] }, { _id: 6, name: 'Dan', reportsTo: 'Andrew', reportingHierarchy: [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' }, { _id: 1, name: 'Dev' }, { _id: 4, name: 'Andrew', reportsTo: 'Eliot' } ] } ]
次の表は、ドキュメント「{ "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }」の走査パスを示しています。
開始値 | ドキュメントの | |
深度 0 | | |
深度 1 | | |
深度 2 | |
出力により、階層「Asya -> Ron -> Eliot -> Dev」が生成されます。
複数のコレクションにわたる場合
$lookup と同様に、$graphLookup も同じデータベース内の別のコレクションにアクセスできます。
たとえば、2 つのコレクションを含むデータベースを作成します。
次のドキュメントを含む
airportsコレクション。db.airports.insertMany( [ { _id: 0, airport: "JFK", connects: [ "BOS", "ORD" ] }, { _id: 1, airport: "BOS", connects: [ "JFK", "PWM" ] }, { _id: 2, airport: "ORD", connects: [ "JFK" ] }, { _id: 3, airport: "PWM", connects: [ "BOS", "LHR" ] }, { _id: 4, airport: "LHR", connects: [ "PWM" ] } ] ) 次のドキュメントを含む
travelersコレクション。db.travelers.insertMany( [ { _id: 1, name: "Dev", nearestAirport: "JFK" }, { _id: 2, name: "Eliot", nearestAirport: "JFK" }, { _id: 3, name: "Jeff", nearestAirport: "BOS" } ] )
travelers コレクションの各ドキュメントで次の集計操作を行った場合、airports コレクションの nearestAirport 値を検索し、connects フィールドと airport フィールドを再帰的に照合します。この操作では、最大再帰深度を 2 とします。
db.travelers.aggregate( [ { $graphLookup: { from: "airports", startWith: "$nearestAirport", connectFromField: "connects", connectToField: "airport", maxDepth: 2, depthField: "numConnections", as: "destinations" } } ] )
[ { _id: 1, name: 'Dev', nearestAirport: 'JFK', destinations: [ { _id: 0, airport: 'JFK', connects: [ 'BOS', 'ORD' ], numConnections: Long('0') }, { _id: 1, airport: 'BOS', connects: [ 'JFK', 'PWM' ], numConnections: Long('1') }, { _id: 3, airport: 'PWM', connects: [ 'BOS', 'LHR' ], numConnections: Long('2') }, { _id: 2, airport: 'ORD', connects: [ 'JFK' ], numConnections: Long('1') } ] }, { _id: 2, name: 'Eliot', nearestAirport: 'JFK', destinations: [ { _id: 0, airport: 'JFK', connects: [ 'BOS', 'ORD' ], numConnections: Long('0') }, { _id: 1, airport: 'BOS', connects: [ 'JFK', 'PWM' ], numConnections: Long('1') }, { _id: 3, airport: 'PWM', connects: [ 'BOS', 'LHR' ], numConnections: Long('2') }, { _id: 2, airport: 'ORD', connects: [ 'JFK' ], numConnections: Long('1') } ] }, { _id: 3, name: 'Jeff', nearestAirport: 'BOS', destinations: [ { _id: 0, airport: 'JFK', connects: [ 'BOS', 'ORD' ], numConnections: Long('1') }, { _id: 1, airport: 'BOS', connects: [ 'JFK', 'PWM' ], numConnections: Long('0') }, { _id: 4, airport: 'LHR', connects: [ 'PWM' ], numConnections: Long('2') }, { _id: 3, airport: 'PWM', connects: [ 'BOS', 'LHR' ], numConnections: Long('1') }, { _id: 2, airport: 'ORD', connects: [ 'JFK' ], numConnections: Long('2') } ] } ]
次の表は、再帰的検索の走査パス(深度 2 まで)を示しています。開始 airport は JFK です。
開始値 |
| ||
深度 0 | | ||
深度 1 | | ||
深度 2 | |
クエリ フィルター付き
次の例では、人物の名前とその友人や趣味の配列が記載された一連のドキュメントを含むコレクションを使用しています。集計操作では、ある特定の人物を検索し、その人脈を走査して、趣味に golf を挙げている人を見つけます。
people という名前のコレクションには次のドキュメントが含まれています。
db.people.insertMany( [ { _id: 1, name: "Tanya Jordan", friends: [ "Shirley Soto", "Terry Hawkins", "Carole Hale" ], hobbies: [ "tennis", "unicycling", "golf" ] }, { _id: 2, name: "Carole Hale", friends: [ "Joseph Dennis", "Tanya Jordan", "Terry Hawkins" ], hobbies: [ "archery", "golf", "woodworking" ] }, { _id: 3, name: "Terry Hawkins", friends: [ "Tanya Jordan", "Carole Hale", "Angelo Ward" ], hobbies: [ "knitting", "frisbee" ] }, { _id: 4, name: "Joseph Dennis", friends: [ "Angelo Ward", "Carole Hale" ], hobbies: [ "tennis", "golf", "topiary" ] }, { _id: 5, name: "Angelo Ward", friends: [ "Terry Hawkins", "Shirley Soto", "Joseph Dennis" ], hobbies: [ "travel", "ceramics", "golf" ] }, { _id: 6, name: "Shirley Soto", friends: [ "Angelo Ward", "Tanya Jordan", "Carole Hale" ], hobbies: [ "frisbee", "set theory" ] } ] )
次の集計操作では、下記の 3 つのステージを使用します。
$matchnameは、string を含む"Tanya Jordan"フィールドを持つドキュメントと一致します。 1 つの出力ドキュメントを返します。$graphLookupは出力ドキュメントのfriendsフィールドをコレクション内の他のドキュメントのnameフィールドと接続して、Tanya Jordan'sの接続ネットワークを走査します。 このステージでは、restrictSearchWithMatchパラメータを使用して、hobbies配列にgolfが含まれるドキュメントのみを検索します。 1 つの出力ドキュメントを返します。$projectは、出力ドキュメントを形成します。connections who play golfのリスト内の名前は、入力ドキュメントのgolfers配列に挙げられているドキュメントのnameフィールドから取得されます。
db.people.aggregate( [ { $match: { "name": "Tanya Jordan" } }, { $graphLookup: { from: "people", startWith: "$friends", connectFromField: "friends", connectToField: "name", as: "golfers", restrictSearchWithMatch: { "hobbies": "golf" } } }, { $project: { "name": 1, "friends": 1, "connections who play golf": "$golfers.name" } } ] )
[ { _id: 1, name: 'Tanya Jordan', friends: [ 'Shirley Soto', 'Terry Hawkins', 'Carole Hale' ], 'connections who play golf': [ 'Tanya Jordan', 'Joseph Dennis', 'Angelo Ward', 'Carole Hale' ] } ]
employees という名前のコレクションには次のドキュメントが含まれています。
{ _id: 1, name: "Dev" }, { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 3, name: "Ron", reportsTo: "Eliot" }, { _id: 4, name: "Andrew", reportsTo: "Eliot" }, { _id: 5, name: "Asya", reportsTo: "Ron" }, { _id: 6, name: "Dan", reportsTo: "Andrew" }
次の Employeeクラスは、employeesコレクション内のドキュメントをモデル化します。
public class Employee { public ObjectId Id { get; set; } public string Name { get; set; } public Employee ReportsTo { get; set; } public List<Employee> ReportingHierarchy { get; set; } public List<string> Hobbies { get; set; } }
MongoDB .NET/ C#ドライバーを使用して$graphLookup ステージを集計パイプラインに追加するには、 PipelineDefinitionオブジェクトで GraphLookup() メソッドを呼び出します。
次の例では、employeesコレクションの ReportsTo フィールドと Name フィールドを再帰的に照合するパイプラインステージを作成し、各人のレポート階層を返します。
var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>( from: employeeCollection, connectFromField: e => e.ReportsTo, connectToField: e => e.Name, startWith: e => e.ReportsTo, @as: e => e.ReportingHierarchy);
AggregateGraphLookupOptionsオブジェクトを使用して、再帰する深度と深度フィールドの名前を指定できます。次のコード例では、前の例と同じ $graphLookup操作を実行しますが、最大再帰深度は 1 に指定しています。
var employeeCollection = client.GetDatabase("aggregation_examples").GetCollection<Employee>("employees"); var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>( from: employeeCollection, connectFromField: e => e.ReportsTo, connectToField: e => e.Name, startWith: e => e.ReportsTo, @as: e => e.ReportingHierarchy, new AggregateGraphLookupOptions<Employee, Employee, Employee> { MaxDepth = 1 });
また、AggregateGraphLookupOptionsオブジェクトを使用して、 MongoDB が検索にドキュメントを含めるために一致する必要があるフィルターを指定することもできます。次のコード例では、前の例と同じ $graphLookup操作を実行しますが、Hobbiesフィールドに "golf" が含まれる Employee ドキュメントのみが含まれます。
var employeeCollection = client.GetDatabase("aggregation_examples").GetCollection<Employee>("employees"); var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>( from: employeeCollection, connectFromField: e => e.ReportsTo, connectToField: e => e.Name, startWith: e => e.ReportsTo, @as: e => e.ReportingHierarchy, new AggregateGraphLookupOptions<Employee, Employee, Employee> { MaxDepth = 1, RestrictSearchWithMatch = Builders<Employee>.Filter.AnyEq(e => e.Hobbies, "golf") });
employees という名前のコレクションには次のドキュメントが含まれています。
db.employees.insertMany([ { _id: 1, name: "Dev" }, { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 3, name: "Ron", reportsTo: "Eliot" }, { _id: 4, name: "Andrew", reportsTo: "Eliot" }, { _id: 5, name: "Asya", reportsTo: "Ron" }, { _id: 6, name: "Dan", reportsTo: "Andrew" } ]);
MongoDB Node.jsドライバーを使用して $graphLookup ステージを集計パイプラインに追加するには、パイプラインオブジェクトで $graphLookup 演算子を使用します。
次の例では、reportsTo フィールドを employees コレクションの name フィールドと再帰的に照合するパイプラインステージを作成し、reportingHierarchy という名前の新規フィールドに各人のレポート作成階層を返します。次に、この例は集計パイプラインを実行します。
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
再帰の深さを指定するには、maxDepth フィールドを使用します。次のコード例は、前の例と同じ$graphLookup 操作を実行しますが、最大再帰深度を 1 に指定します。
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy", maxDepth: 1 } } ]; const cursor = collection.aggregate(pipeline); return cursor;
操作でドキュメントが検索結果に含まれるために一致する必要があるフィルターを指定するには、restrictSearchWithMatch フィールドを使用します。次のコード例では、前の例と同じ $graphLookup 操作を実行しますが、hobbiesフィールドに "golf" が含まれる employee ドキュメントのみを含みます。
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy", maxDepth: 1, restrictSearchWithMatch: { hobbies: "golf" } } } ]; const cursor = collection.aggregate(pipeline); return cursor;
詳細
$graphLookup の使用方法の詳細については、ウェビナー:MongoDBでのグラフデータの操作を参照してください。