Definición
$graphLookupCambiado en la versión 5.1.
Realiza una búsqueda recursiva en una colección, con opciones para restringir la búsqueda por profundidad de recursión y filtro de query.
El proceso de búsqueda
$graphLookupse resume a continuación:Los documentos de entrada fluyen a la etapa
$graphLookupde una operación de agregación.$graphLookupdirige la búsqueda a la colección designada por el parámetrofrom(consulte a continuación la lista completa de parámetros de búsqueda).Para cada documento de entrada, la búsqueda comienza con el valor designado por
startWith.$graphLookupcompara el valorstartWithcon el campo designado porconnectToFielden otros documentos de la colecciónfrom.Para cada documento coincidente,
$graphLookuptoma el valor deconnectFromFieldy verifica cada documento en la colecciónfrompara un valor deconnectToFieldcoincidente. Para cada coincidencia,$graphLookupagrega el documento coincidente de la colecciónfroma un campo de arreglo nombrado por el parámetroas.Este paso continúa de forma recursiva hasta que no se encuentren más documentos coincidentes o hasta que la operación alcance una profundidad de recursión especificada por el parámetro
maxDepth.$graphLookupluego agrega el campo del arreglo al documento de entrada.$graphLookupdevuelve resultados después de completar su búsqueda en todos los documentos de entrada.
$graphLookuptiene el siguiente prototipo:{ $graphLookup: { from: <collection>, startWith: <expression>, connectFromField: <string>, connectToField: <string>, as: <string>, maxDepth: <number>, depthField: <string>, restrictSearchWithMatch: <document> } } $graphLookuptoma un documento con los siguientes campos:CampoDescripciónfromColección de objetivos para el
$graphLookupOperación para buscar, haciendo coincidir recursivamenteconnectFromFieldconnectToFieldcon. Lafromcolección debe estar en la misma base de datos que cualquier otra colección utilizada en la operación.A partir de MongoDB 5.1, la colección especificada en el parámetro
frompuede ser fragmentada.startWithExpresión que especifica el valor del
connectFromFieldcon el cual iniciar la búsqueda recursiva. SistartWithse evalúa como un arreglo,$graphLookuprealiza la búsqueda simultáneamente en todos los elementos del arreglo.connectFromFieldNombre del campo cuyo valor
$graphLookupse utiliza para hacer coincidir recursivamente con elconnectToFieldde otros documentos en la colección. Si el valor es un arreglo, cada elemento se sigue individualmente a través del proceso de recorrido.connectToFieldNombre del campo en otros documentos con el que se debe comparar el valor del campo especificado por el parámetro
connectFromField.asNombre del campo de arreglo añadido a cada documento de salida. Contiene los documentos que se han recorrido en la etapa
$graphLookuppara llegar al documento.No se ofrecen garantías de que los documentos devueltos en el campo
asestén en ningún orden específico.maxDepthOpcional. Número entero no negativo que especifica la profundidad máxima de recursión.
depthFieldOpcional. Nombre del campo que se debe añadir a cada documento recorrido en la ruta de búsqueda. El valor de este campo es la profundidad de recursión del documento, representado como un
NumberLong. El valor de la profundidad de recursión comienza en cero, por lo que la primera búsqueda corresponde a una profundidad de cero.restrictSearchWithMatchOpcional. Un documento que especifica condiciones adicionales para la búsqueda recursiva. La sintaxis es idéntica a la sintaxis de filtro de query.
No se puede usar ninguna expresión de agregación en este filtro. Por ejemplo, no puede utilizar el siguiente documento para encontrar documentos en los que el valor
lastNamesea distinto del valorlastNamedel documento de entrada:{ lastName: { $ne: "$lastName" } } No se puede utilizar el documento en este contexto, porque
"$lastName"actuará como un literal de string, no como una ruta de campo.
Considerations
Colecciones fragmentadas
A partir de MongoDB 5.1, puedes especificar sharded collections en el parámetro from de las etapas de $graphLookup.
No se puede usar la etapa $graphLookup dentro de una transacción durante el direccionamiento a una colección fragmentada.
Profundidad máxima
Configurar el campo maxDepth a 0 es equivalente a una etapa de búsqueda no recursiva $graphLookup.
Memoria
Si la etapa $graphLookup consume más de 100 megabytes de memoria, automáticamente guarda archivos temporales en el disco. Se puede ver cuándo $graphLookup usa el disco mediante el comando serverStatus y ver una explicación del uso del disco de $graphLookup mediante el comando explain() en nivel de verbosidad executionStats.
Si la etapa$graphLookup supera los 100 megabytes de memoria y la opción allowDiskUse está configurada en false, $graphLookup devuelve un error.
Consulte las limitaciones del pipeline de agregación para obtener más información.
Resultados no ordenados
La etapa $graphLookup no devuelve resultados ordenados. Para ordenar sus resultados, utilice el operador $sortArray.
Vistas e intercalación
Si se realiza una agregación que implique varias vistas, como con $lookup o $graphLookup, las vistas deben tener la misma intercalación.
Ejemplos
Dentro de una única colección
Una colección llamada employees tiene los siguientes 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" } ] )
La siguiente operación $graphLookup coincide de forma recursiva en los campos reportsTo y name de la colección employees, devolviendo la jerarquía de reportes para cada persona:
db.employees.aggregate( [ { $graphLookup: { from: "employees", startWith: "$reportsTo", connectFromField: "reportsTo", connectToField: "name", as: "reportingHierarchy" } } ] )
La salida se asemeja a los siguientes 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" } ] }
La siguiente tabla proporciona una ruta de acceso para el documento { "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }:
Valor inicial | El valor | |
Profundidad 0 | | |
Profundidad 1 | | |
Profundidad 2 | |
El resultado genera la jerarquía Asya -> Ron -> Eliot -> Dev.
A través de múltiples colecciones
Al igual que $lookup, $graphLookup puede acceder a otra colección en la misma base de datos.
Por ejemplo, se puede crear una base de datos con dos colecciones:
Una colección
airportscon los siguientes 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" ] } ] ) Una colección
travelerscon los siguientes 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 en la colección travelers, la siguiente operación de agregación busca el valor nearestAirport en la colección airports y coincide recursivamente el campo connects con el campo airport. La operación especifica una profundidad máxima de recursión de 2.
db.travelers.aggregate( [ { $graphLookup: { from: "airports", startWith: "$nearestAirport", connectFromField: "connects", connectToField: "airport", maxDepth: 2, depthField: "numConnections", as: "destinations" } } ] )
La salida se asemeja a los siguientes 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) } ] }
La siguiente tabla proporciona una ruta de recorrido para la búsqueda recursiva, hasta la profundidad 2, donde el airport inicial es JFK:
Valor inicial | El valor | ||
Profundidad 0 | | ||
Profundidad 1 | | ||
Profundidad 2 | |
Con un filtro de query
El siguiente ejemplo utiliza una colección con un conjunto de documentos que contienen nombres de personas junto con arreglos de sus amigos y sus aficiones. Una operación de agregación encuentra a una persona en particular y recorre su red de conexiones para encontrar personas que incluyan golf entre sus aficiones.
Una colección llamada people contiene los siguientes 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" ] } ] )
La siguiente operación de agregación utiliza tres etapas:
$matchcoincide en documentos con un camponameque contiene la string"Tanya Jordan". Devuelve un documento de salida.$graphLookupconecta el campofriendsdel documento de salida con el camponamede otros documentos de la colección para recorrer la red de conexionesTanya Jordan's. Esta etapa utiliza el parámetrorestrictSearchWithMatchpara encontrar solo documentos en los que el arreglohobbiescontienegolf. Devuelve un documento de salida.$projectda forma al documento de salida. Los nombres listados enconnections who play golfse extraen del camponamede los documentos enumerados en el arreglogolfersdel 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" } } ] )
La operación devuelve el siguiente 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" ] }
Una colección llamada employees tiene los siguientes 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" }
La siguiente clase Employee modela los documentos en la colección 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 el controlador MongoDB.NET/C# para agregar una $graphLookup etapa a una canalización de agregación, llame a GraphLookup()método en un PipelineDefinition objeto.
El siguiente ejemplo crea una etapa de pipeline que coincide recursivamente en los campos ReportsTo y Name de la colección employees, devolviendo la jerarquía de reporte para cada persona:
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);
Se puede usar un objeto AggregateGraphLookupOptions para especificar la profundidad de recursión y el nombre del campo de profundidad. El siguiente ejemplo de código realiza la misma operación $graphLookup que el ejemplo anterior, pero especifica una profundidad máxima de recursión 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 });
También se puede utilizar un objeto AggregateGraphLookupOptions para especificar un filtro que los documentos deben hacer coincidir para que MongoDB los incluya en su búsqueda. El siguiente ejemplo de código realiza la misma operación $graphLookup que los ejemplos anteriores, pero solo incluye los documentos Employee en los que el campo Hobbies contiene "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") });
Una colección llamada employees tiene los siguientes 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 utilizar el controlador de MongoDB Node.js para agregar una etapa de $graphLookup a una canalización de agregación, utilice el Operador $graphLookup en un objeto de canalización.
El siguiente ejemplo crea una etapa de pipeline que hace coincidir recursivamente los campos reportsTo con los campos name en la colección employees, devolviendo la jerarquía de reportes para cada persona en un nuevo campo llamado reportingHierarchy. A continuación, el ejemplo ejecuta el pipeline de agregación:
const pipeline = [ { $graphLookup: { from: "employees", connectFromField: "reportsTo", connectToField: "name", startWith: "$reportsTo", as: "reportingHierarchy" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Para especificar la profundidad de la recursión, se puede utilizar el campo maxDepth. El siguiente ejemplo de código realiza la misma operación $graphLookup que el anterior ejemplo, pero especifica una profundidad máxima de recursión 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 un filtro que los documentos deben cumplir para que la operación los incluya en los resultados de búsqueda, utilice el campo restrictSearchWithMatch. El siguiente ejemplo de código realiza la misma operación $graphLookup que los ejemplos anteriores, pero solo incluye los documentos employee en los que el campo hobbies contiene "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;
Obtén más información
Para aprender más sobre cómo utilizar $graphLookup, consulte el seminario web: Trabajando con datos de grafos en MongoDB.