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çãofromColeção de destino para a
$graphLookupoperação a ser procurada, combinando recursivamente o comconnectFromFieldconnectToFieldo. A collectionfromdeve estar no mesmo banco de dados que qualquer outra collection usada na operação.A partir do MongoDB 5.1, a coleção especificada no parâmetro
frompode ser fragmentada.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.connectFromFieldNome do campo cujo valor usa para corresponder recursivamente
$graphLookupaoconnectToFieldde outros documentos na coleção. Se o valor for uma matriz, cada elemento será seguido individualmente durante o processo transversal.connectToFieldNome do campo em outros documentos para corresponder ao valor do campo especificado pelo parâmetro
connectFromField.asNome do campo de array adicionado a cada documento de saída. Contém os documentos percorridos no estágio para chegar ao
$graphLookupdocumento.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 coleções fragmentadas no parâmetro from 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
Definir o campo maxDepth com 0 equivale a um estágio da pesquisa $graphLookup não recursiva.
Memória
A etapa $graphLookup deve permanecer dentro do limite de memória de 100 megabytes. Se o allowDiskUse: true for especificado para a operação aggregate(), a etapa $graphLookup ignorará a opção. Se houver outras etapas na operação do aggregate(), a opção allowDiskUse: true estará em vigor para estas outras etapas.
Consulte as limitações 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
Se estiver realizando uma agregação que envolva várias exibições, como com $lookup ou $graphLookup, as exibições deverão ter o mesmo agrupamento.
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" } ] )
A seguinte operação $graphLookup corresponde recursivamente nos campos reportsTo e name na coleção employees, retornando a hierarquia de relatórios de cada pessoa:
db.employees.aggregate( [ { $graphLookup: { from: "employees", startWith: "$reportsTo", connectFromField: "reportsTo", connectToField: "name", as: "reportingHierarchy" } } ] )
A saída se assemelha aos seguintes resultados:
{ _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: 4, name: "Andrew", reportsTo: "Eliot" }, { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 1, name: "Dev" } ] }
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
Como o $lookup, o $graphLookup pode acessar outra coleção no mesmo banco de dados.
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" } } ] )
A saída se assemelha aos seguintes resultados:
{ _id: 1, name: "Dev", nearestAirport: "JFK", destinations: [ { _id: 3, airport: "PWM", connects: [ "BOS", "LHR" ], numConnections: Long(2) }, { _id: 2, airport: "ORD", connects: [ "JFK" ], numConnections: Long(1) }, { _id: 1, airport: "BOS", connects: [ "JFK", "PWM" ], numConnections: Long(1) }, { _id: 0, airport: "JFK", connects: [ "BOS", "ORD" ], numConnections: Long(0) } ] } { _id: 2, name: "Eliot", nearestAirport: "JFK", destinations: [ { _id: 3, airport: "PWM", connects: [ "BOS", "LHR" ], numConnections: Long(2) }, { _id: 2, airport: "ORD", connects: [ "JFK" ], numConnections: Long(1) }, { _id: 1, airport: "BOS", connects: [ "JFK", "PWM" ], numConnections: Long(1) }, { _id: 0, airport: "JFK", connects: [ "BOS", "ORD" ], numConnections: Long(0) } ] } { "_id" : 3, name: "Jeff", nearestAirport: "BOS", destinations: [ { _id: 2, airport: "ORD", connects: [ "JFK" ], numConnections: Long(2) }, { _id: 3, airport: "PWM", connects: [ "BOS", "LHR" ], numConnections: Long(1) }, { _id: 4, airport: "LHR", connects: [ "PWM" ], numConnections: Long(2) }, { _id:: 0, airport: "JFK", connects: [ "BOS", "ORD" ], numConnections: Long(1) }, { _id:: 1, airport: "BOS", connects: [ "JFK", "PWM" ], numConnections: Long(0) } ] }
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 documento de saída.$graphLookupconecta o campofriendsdo documento de saída com o camponamede outros documentos na coleção para atravessar a rede de conexões deTanya Jordan'sEsse estágio utiliza o parâmetrorestrictSearchWithMatchpara encontrar somente documentos nos quais o arrayhobbiescontémgolf. Retorna um documento de saída.$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" } } ] )
A operação retorna o seguinte documento:
{ _id: 1, name: "Tanya Jordan", friends: [ "Shirley Soto", "Terry Hawkins", "Carole Hale" ], 'connections who play golf': [ "Joseph Dennis", "Tanya Jordan", "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 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; } }
Para usar o driver MongoDB .NET/C# para adicionar um estágio $graphLookup a um pipeline de agregação, chame o método GraphLookup() em um objeto PipelineDefinition.
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, Employee, string, Employee, List<Employee>, Employee>( from: employeeCollection, connectFromField: e => e.ReportsTo, connectToField: e => e.Name, startWith: e => e.ReportsTo, @as: e => e.ReportingHierarchy);
Você pode usar um objeto AggregateGraphLookupOptions para especificar a profundidade da recursão e o nome do campo de profundidade. O exemplo de código a seguir executa a mesma operação $graphLookup do exemplo anterior , mas especifica uma profundidade máxima de recursão de 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 });
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 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") });
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
Para aprender mais sobre como usar o $graphLookup, consulte o webinar: Trabalhando com dados de grafo no MongoDB.