Definição
$graphLookupAlterado na versão 5.1.
Executa uma pesquisa recursiva em uma coleção, com opções para restringir a pesquisa por profundidade de recursão e filtro de consulta.
O processo de pesquisa
$graphLookupestá resumido abaixo:Documentos de entrada fluem para o estágio
$graphLookupde uma operação de agregação.$graphLookupdireciona a pesquisa para a coleção designada pelo parâmetrofrom(veja abaixo a lista completa de parâmetros de pesquisa).Para cada documento de entrada, a pesquisa começa com o valor designado por
startWith.$graphLookupcorresponde ao valorstartWithcontra o campo designado porconnectToFieldem outros documentos na coleçãofrom.Para cada documento correspondente,
$graphLookuppega o valor deconnectFromFielde verifica cada documento na coleçãofromem busca de um valor correspondente aconnectToField. Para cada correspondência,$graphLookupadiciona o documento correspondente na coleçãofroma um campo de array nomeado pelo parâmetroas.Essa etapa continua recursivamente até que não haja mais documentos correspondentes encontrados, ou até que a operação atinja uma profundidade de recursão especificada pelo parâmetro
maxDepth.$graphLookupentão acrescenta o campo de array ao documento de entrada.$graphLookupretorna os resultados após concluir sua pesquisa em todos os documentos de entrada.
$graphLookuptem a seguinte forma de protótipo:{ $graphLookup: { from: <collection>, startWith: <expression>, connectFromField: <string>, connectToField: <string>, as: <string>, maxDepth: <number>, depthField: <string>, restrictSearchWithMatch: <document> } } $graphLookupobtém um documento com os seguintes campos:CampoDescriçãofromTarget collection for the
$graphLookupoperation to search, recursively matching theconnectFromFieldto theconnectToField. Thefromcollection must be in the same database as any other collections used in the operation.For sharding support, see Sharded Collections.
startWithExpressão que especifica o valor do
connectFromFieldcom o qual iniciar a pesquisa recursiva. SestartWithavaliar para uma array,$graphLookupexecuta a pesquisa simultaneamente a partir de todos os elementos da array.connectFromFieldField name whose value
$graphLookupuses to recursively match against theconnectToFieldof other documents in the collection. If the value is an array, each element is individually followed through the traversal process.connectToFieldNome do campo em outros documentos para corresponder ao valor do campo especificado pelo parâmetro
connectFromField.asName of the array field added to each output document. Contains the documents traversed in the
$graphLookupstage to reach the document.Não é garantido que os documentos retornados no campo
asestejam em qualquer ordem.maxDepthOpcional. Número integral não negativo, especificando a profundidade máxima da recursão.
depthFieldOpcional. Nome do campo a ser adicionado a cada documento atravessado no caminho de pesquisa. O valor deste campo é a profundidade de recursão do documento, representado como um
NumberLong. O valor da profundidade de recursão começa em zero, então a primeira pesquisa corresponde à profundidade zero.restrictSearchWithMatchOpcional. Um documento que especifica condições adicionais para a pesquisa recursiva. A sintaxe é idêntica à sintaxe do filtro de consulta.
Você não pode utilizar qualquer expressão de agregação neste filtro. Por exemplo, você não pode usar o seguinte documento para localizar documentos nos quais o valor
lastNameé diferente do valorlastNamedo documento de entrada:{ lastName: { $ne: "$lastName" } } Você não pode usar o documento neste contexto, porque
"$lastName"atuará como uma string literalmente, e não como um caminho do campo.
Considerações
Coleções fragmentadas
A partir do MongoDB,5.1 você pode especificar collections fragmentadas no from parâmetro de $graphLookup estágios.
Você não pode usar a etapa $graphLookup dentro de uma transação enquanto segmenta uma coleção fragmentada.
Profundidade máxima
Setting the maxDepth field to 0 is equivalent to a non-recursive $graphLookup search stage.
Memória
O estágio$graphLookupdeve ficar dentro do limite de memória 100 megabyte. Se o allowDiskUse: true for especificado para a operaçãoaggregate(), o estágio$graphLookupignorará a opção. Se houver outros estágios na operaçãoaggregate(), a opção allowDiskUse: true estará em vigor para esses outros estágios.
Consulte limites do pipeline de agregação para obter mais informações.
Resultados não ordenados
O estágio $graphLookup não retorna resultados ordenados. Para classificar seus resultados, use o $sortArray operador.
Visualizações e agrupamento
If performing an aggregation that involves multiple views, such as with $lookup or $graphLookup, the views must have the same collation.
Exemplos
Em uma única coleção
Uma coleção chamada employees possui os seguintes documentos:
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" } ] )
The following $graphLookup operation recursively matches on the reportsTo and name fields in the employees collection, returning the reporting hierarchy for each person:
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' } ] } ]
A tabela a seguir fornece um caminho transversal para o documento { "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }:
Valor inicial | O valor | |
Profundidade 0 | | |
Profundidade 1 | | |
Profundidade 2 | |
O resultado gera a hierarquia Asya -> Ron -> Eliot -> Dev.
Em várias coleções
Like $lookup, $graphLookup can access another collection in the same database.
Por exemplo, crie um banco de dados com duas coleções:
Uma coleção
airportscom os seguintes documentos: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" ] } ] ) Uma coleção
travelerscom os seguintes documentos:db.travelers.insertMany( [ { _id: 1, name: "Dev", nearestAirport: "JFK" }, { _id: 2, name: "Eliot", nearestAirport: "JFK" }, { _id: 3, name: "Jeff", nearestAirport: "BOS" } ] )
Para cada documento na coleção travelers, a seguinte operação de agregação procura o valor nearestAirport na coleção airports e recursivamente corresponde ao campo connects ao campo airport. A operação especifica uma profundidade máxima de recursão de 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') } ] } ]
A tabela a seguir fornece um caminho transversal para a pesquisa recursiva, até a profundidade 2, onde o airport inicial é JFK:
Valor inicial | O valor | ||
Profundidade 0 | | ||
Profundidade 1 | | ||
Profundidade 2 | |
Com um filtro de consulta
O exemplo a seguir usa uma coleção com um conjunto
de documentos que contêm nomes de pessoas juntamente com arrayes de seus amigos
e seus hobbies. Uma operação de agregação encontra uma pessoa em particular e atravessa sua rede de conexões para encontrar pessoas que listam o golf entre seus hobbies.
Uma coleção chamada people contém os seguintes documentos:
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" ] } ] )
A seguinte operação de agregação utiliza três estágios:
$matchcorresponde a documentos com um camponamecontendo a string"Tanya Jordan". Retorna um output document.$graphLookupconnects the output document'sfriendsfield with thenamefield of other documents in the collection to traverseTanya Jordan'snetwork of connections. This stage uses therestrictSearchWithMatchparameter to find only documents in which thehobbiesarray containsgolf. Returns one output document.$projectmolda o documento de saída. Os nomes listados emconnections who play golfsão retirados do camponamedos documentos listados na arraygolfersdo documento de entrada.
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' ] } ]
Uma coleção chamada employees possui os seguintes documentos:
{ _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" }
Os seguintes documentos de modelos de classe Employee na coleção employees:
[] public class Employee { [] public int Id { get; set; } [] public string Name { get; set; } = null!; [] public string? ReportsTo { get; set; } [] public List<string> Hobbies { get; set; } = new(); [] public List<Employee> ReportingHierarchy { get; set; } = new(); }
To use the MongoDB .NET/C# driver to add a $graphLookup stage to an aggregation pipeline, call the UnionWith() method on a PipelineDefinition object.
O exemplo a seguir cria um estágio de pipeline que corresponde recursivamente aos campos ReportsTo e Name na collection employees, retornando a hierarquia de relatórios para cada pessoa:
var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, string, string, string, List<Employee>, Employee>( from: _collection, connectFromField: e => e.ReportsTo!, connectToField: e => e.Name, startWith: e => e.ReportsTo!, @as: e => e.ReportingHierarchy);
You can use an AggregateGraphLookupOptions object to specify the depth to recurse and the name of the depth field. The following code example performs the same $graphLookup operation as the previous example, but specifies a maximum recursion depth of 1:
var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, string, string, string, List<Employee>, Employee>( from: _collection, connectFromField: e => e.ReportsTo!, connectToField: e => e.Name, startWith: e => e.ReportsTo!, @as: e => e.ReportingHierarchy, new AggregateGraphLookupOptions<Employee, Employee, Employee> { MaxDepth = 1 });
Você também pode usar um objeto AggregateGraphLookupOptions para especificar um filtro ao qual os documentos devem corresponder para que o MongoDB os inclua em sua pesquisa. O exemplo de código a seguir executa a mesma operação $graphLookup dos exemplos anteriores, mas inclui somente documentos Employee em que o campo Hobbies contém "golf":
var pipeline = new EmptyPipelineDefinition<Employee>() .GraphLookup<Employee, Employee, Employee, string, string, string, List<Employee>, Employee>( from: _collection, 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") });
Uma coleção chamada employees possui os seguintes documentos:
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" } ]);
Para usar o driver Node.js do MongoDB para adicionar um estágio $graphLookup a um pipeline de agregação , use o operador $graphLookup em um objeto de pipeline.
O exemplo a seguir cria um estágio de pipeline que combina recursivamente os campos reportsTo aos campos name na coleção employees, retornando a hierarquia de relatórios para cada pessoa em um novo campo chamado reportingHierarchy. O exemplo em seguida executa o pipeline de agregação:
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Para especificar a profundidade da recursão, utilize o campo maxDepth. O exemplo de código a seguir realiza a mesma operação $graphLookup que o exemplo anterior, mas define uma profundidade máxima de recursão de 1:
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy", maxDepth: 1 } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Para especificar um filtro que os documentos devem corresponder para que a operação os inclua nos resultados da pesquisa, use o campo restrictSearchWithMatch. O exemplo de código a seguir realiza a mesma operação $graphLookup que os exemplos anteriores, mas inclui apenas os documentos employee em que o campo hobbies contém "golf":
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;
Saiba mais
To learn more about how to use $graphLookup, see Working with Graph Data in MongoDB.