Definición
$lookupCambiado en la versión 5.1.
Realiza una combinación externa izquierda con una colección en la misma base de datos para filtrar documentos de la colección externa para su procesamiento. La
$lookupstage añade un nuevo campo de arreglo a cada documento de entrada. El nuevo campo arreglo contiene los documentos coincidentes de la colección externa. La$lookupetapa pasa estos documentos remodelados a la siguiente etapa.A partir de MongoDB 5.1, puedes usar
$lookupcon colecciones sharded.Para combinar elementos de dos colecciones diferentes, utiliza el
$unionWithetapa del pipeline.Importante
El uso excesivo de
$lookuppuede ralentizar el rendimiento de las query. Para reducir la dependencia de$lookup, considera un modelo de datos embebido para almacenar datos relacionados en una sola colección.Para obtener detalles sobre el rendimiento de
$lookup, consulta Consideraciones sobre el rendimiento.
Compatibilidad
Puedes usar $lookup para implementaciones alojadas en los siguientes entornos:
MongoDB Atlas: El servicio totalmente gestionado para implementaciones de MongoDB en la nube
MongoDB Enterprise: La versión basada en suscripción y autogestionada de MongoDB
MongoDB Community: La versión de MongoDB con código fuente disponible, de uso gratuito y autogestionada.
Sintaxis
La sintaxis de la etapa $lookup:
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run> ], as: <output array field> } }
$lookup acepta un documento con estos campos:
Campo | Necesidad | Descripción |
|---|---|---|
Requerido | Especifica la colección foránea en la misma base de datos para unirse a la colección local. Es posible en algunos casos extremos sustituir A partir de MongoDB 5.1, la colección | |
Opcional si se especifica | Especifica el campo de la entrada de documentos para la etapa | |
Opcional si se especifica | Especifica los Si un documento foráneo no contiene un valor | |
Opcional | Especifica las variables que se deben usar en las etapas de la pipeline. Utiliza las expresiones de variables para acceder a los campos de los documentos de la colección local que se ingresan en Para referenciar variables en las etapas de un pipeline, utiliza la sintaxis Se puede acceder a las variables let por las etapas de la pipeline, incluidas las etapas adicionales
| |
Opcional si se especifican | Especifica el El El Para referenciar variables en las etapas de un pipeline, utiliza la sintaxis Se puede acceder a las variables let por las etapas de la pipeline, incluidas las etapas adicionales
| |
Requerido | Especifica el nombre del nuevo campo de arreglo que se añadirá a los documentos de entrada. El nuevo campo de arreglo contiene los documentos coincidentes de la colección |
Coincidencia exacta con una única condición de unión
Para realizar una coincidencia exacta entre un campo de los documentos de entrada y un campo de los documentos de la colección externa, la etapa $lookup tiene esta sintaxis:
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, pipeline: [ <pipeline to run> ], as: <output array field> } }
Nota
En este ejemplo, pipeline es opcional y se ejecuta después de la etapa de igualdad local y foránea.
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, ( SELECT ARRAY_AGG(*) FROM <collection to join> WHERE <foreignField> = <collection.localField> ) AS <output array field> FROM collection;
Nota
Las instrucciones SQL de esta página se incluyen para compararlas con la sintaxis de la pipeline de agregación de MongoDB. Las instrucciones SQL no son ejecutables.
Para ejemplos de MongoDB, consulte estas páginas:
Condiciones de unión y sub-query en una colección foránea
MongoDB admite:
Ejecución de una pipeline en una colección externa.
Múltiples condiciones de combinación.
Subconsultas correlacionadas y no correlacionadas.
En MongoDB, un sub-query no correlacionado significa que cada documento de entrada arrojará el mismo resultado. Un sub-query correlacionado es un pipeline en una etapa$lookup que utiliza los campos de la colección local o input para arrojar resultados correlacionados con cada documento entrante.
Nota
A partir de MongoDB 5.0, para un subquery no correlacionado en una etapa de la pipeline de $lookup que contenga una etapa $sample, el operador $sampleRate o el operador $rand, el subquery siempre se ejecuta nuevamente si se repite. Anteriormente, según el tamaño del resultado del subquery, se almacenaba en caché o el subquery se ejecutaba nuevamente.
Las sub-query correlacionadas de MongoDB son comparables a las sub-query correlacionadas de SQL, donde la query interna hace referencia a los valores externos de la query. Una sub-query no correlacionada de SQL no hace referencia a valores de la query externa.
MongoDB 5.0 también soporta a los subquery correlacionados concisos.
Para realizar los subquery correlacionados y no correlacionados con dos colecciones, y realizar otras condiciones de unión además de una única coincidencia exacta, utiliza esta sintaxis de $lookup:
{ $lookup: { from: <foreign collection>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run on foreign collection> ], as: <output array field> } }
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, <output array field> FROM collection WHERE <output array field> IN ( SELECT <documents as determined from the pipeline> FROM <collection to join> WHERE <pipeline> );
Vea los siguientes ejemplos:
Subconsultas correlacionadas utilizando una sintaxis concisa
Nuevo en la versión 5.0.
A partir de MongoDB 5.0, puedes usar una sintaxis concisa para una sub-query correlacionada. Las sub-query correlacionadas hacen referencia a campos de documentos de una colección foránea y de la colección "local" en la que se ejecutó el método aggregate().
La siguiente nueva sintaxis concisa elimina el requisito de una coincidencia exacta en los campos foráneo y local dentro de un operador $expr:
{ $lookup: { from: <foreign collection>, localField: <field from local collection's documents>, foreignField: <field from foreign collection's documents>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run> ], as: <output array field> } }
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, <output array field> FROM localCollection WHERE <output array field> IN ( SELECT <documents as determined from the pipeline> FROM <foreignCollection> WHERE <foreignCollection.foreignField> = <localCollection.localField> AND <pipeline match condition> );
Vea este ejemplo:
Comportamiento
Vistas e intercalación
Si realizas una agregación que implique varias vistas, como con $lookup o $graphLookup, las vistas deben tener la misma intercalación.
Restricciones
No puedes incluir la etapa $out o la etapa $merge en la etapa $lookup. Es decir, al especificar un pipeline para la colección foránea, no puedes incluir ninguna de las etapas en el campo pipeline.
{ $lookup: { from: <collection to join>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to execute on the foreign collection> ], // Cannot include $out or $merge as: <output array field> } }
Soporte de Atlas Search
A partir de MongoDB 6.0, puedes especificar la etapa de Atlas Search $search o $searchMeta en la $lookup pipeline para buscar colecciones en el clúster de Atlas. La $search o la $searchMeta etapa debe ser la primera etapa dentro del pipeline $lookup.
Por ejemplo, cuando unes condiciones y sub-query en una colección foránea o ejecutas sub-query correlacionados usando una sintaxis concisa, puedes especificar $search o $searchMeta dentro del pipeline, como se muestra a continuación:
Para ver un ejemplo de $lookup con $search, consulta el tutorial de Atlas Search Ejecutar un Atlas Search $search Query utilizando $lookup.
Colecciones fragmentadas
A partir de MongoDB 5.1, puedes especificar sharded collections en el parámetro from de las etapas de $lookup.
No se puede usar la etapa $lookup dentro de una transacción durante el direccionamiento a una colección fragmentada.
Motor de ejecución de query basado en ranuras
Nota
A partir de la versión 7.0.17, el motor de ejecución de query basado en slots deja de estar habilitado por defecto para las versiones de parches de 7.0. Si quieres que tus consultas usen el motor de ejecución de consultas basado en slots, por favor actualiza a la versión 8.0, donde está habilitado por defecto.
A partir de la versión 6.0, MongoDB puede usar el motor de query de ejecución basado en ranuras para ejecutar las etapas $lookup si todas las etapas anteriores en el pipeline también se pueden ejecutar por el motor de ejecución basado en ranuras y ninguna de las siguientes condiciones es verdadera:
La operación
$lookupejecuta una pipeline en una colección externa. Para ver un ejemplo de este tipo de operación, consulta Condiciones de unión y subconsultas en una colección externa.Los
localFieldoforeignFieldde$lookupespecifican componentes numéricos. Por ejemplo:{ localField: "restaurant.0.review" }.El campo
fromde cualquier$lookupen el pipeline especifica una vista o una colección fragmentada.
Para obtener más información, consulta $lookupOptimización.
Consideraciones sobre el rendimiento
$lookup el rendimiento depende del tipo de operación que se realice. Consulta la siguiente tabla para las consideraciones de rendimiento de las diferentes operaciones de $lookup.
$lookup Operación | Consideraciones sobre el rendimiento |
|---|---|
| |
| |
|
Para estrategias generales de rendimiento, consulta las Estrategias de indexación y Optimización de query.
Ejemplos
Los ejemplos de esta página utilizan datos del conjunto de datos de muestra sample_mflix. Para obtener más información sobre cómo cargar este conjunto de datos en la implementación autogestionada de MongoDB, consultar Cargar el conjunto de datos de muestra. Si se realizó alguna modificación en las bases de datos de muestra, es posible que se deban descartar y volver a crear las bases de datos para ejecutar los ejemplos de esta página.
Realice una única unión de igualdad con $lookup
La siguiente operación de agregación primero filtra la colección movies para películas con un runtime superior a 1000, y luego se une con la colección comments en los campos _id y movie_id:
db.movies.aggregate( [ { $match: { runtime: { $gt: 1000 } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "movie_comments" } }, { $project: { _id: 0, title: 1, year: 1, "movie_comments.name": 1, "movie_comments.text": 1, "movie_comments.date": 1 } } ] )
[ { title: 'Centennial', year: 1978, movie_comments: [ { name: 'Ellaria Sand', text: 'Excepturi nam nam eum possimus aspernatur autem. Quis nulla optio praesentium ut distinctio explicabo.', date: ISODate('1995-08-18T03:01:50.000Z') } ] }, { title: 'Baseball', year: 1994, movie_comments: [] } ]
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, movie_comments FROM movies WHERE movie_comments IN ( SELECT * FROM comments WHERE movie_id = movies._id );
Para obtener más información, consulta Consideraciones sobre el rendimiento de la coincidencia exacta.
Usar $lookup con un arreglo
Si el localField es un arreglo, puedes comparar los elementos del arreglo contra un escalar foreignField sin una etapa de $unwind.
La siguiente operación de agregación une la colección movies con la colección users, emparejando el campo de arreglo cast de movies con el campo escalar name de users:
db.movies.aggregate( [ { $match: { title: { $in: [ "Roger & Me", "The Sum of Us", "Centennial" ] } } }, { $lookup: { from: "users", localField: "cast", foreignField: "name", as: "cast_users" } }, { $project: { _id: 0, title: 1, year: 1, cast: 1, "cast_users.name": 1, "cast_users.email": 1 } }, { $sort: { year: 1 } } ] )
[ { cast: [ 'Raymond Burr', 'Barbara Carrera', 'Richard Chamberlain', 'Robert Conrad' ], title: 'Centennial', year: 1978, cast_users: [] }, { cast: [ 'Michael Moore', 'Roger B. Smith', 'Rhonda Britton', 'Fred Ross' ], title: 'Roger & Me', year: 1989, cast_users: [ { name: 'Michael Moore', email: 'michael_moore@fakegmail.com' } ] }, { cast: [ 'Jack Thompson', 'Russell Crowe', 'John Polson', 'Deborah Kennedy' ], title: 'The Sum of Us', year: 1994, cast_users: [ { name: 'Deborah Kennedy', email: 'deborah_kennedy@fakegmail.com' } ] } ]
Usa $lookup con $mergeObjects
El $mergeObjects operador combina múltiples documentos en un único documento.
La siguiente operación utiliza $lookup para unir la colección movies con la colección comments, y luego utiliza $mergeObjects en $replaceRoot para combinar el primer document de comentario con el document de película:
db.movies.aggregate( [ { $match: { runtime: { $gt: 1000 } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "movie_comments" } }, { $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$movie_comments", 0 ] }, "$$ROOT" ] } } }, { $project: { _id: 0, title: 1, year: 1, genres: 1, name: 1, email: 1, text: 1, date: 1 } } ] )
[ { name: 'Ellaria Sand', email: 'indira_varma@gameofthron.es', text: 'Excepturi nam nam eum possimus aspernatur autem. Quis nulla optio praesentium ut distinctio explicabo.', date: ISODate('1995-08-18T03:01:50.000Z'), genres: [ 'Action', 'Adventure', 'Drama' ], title: 'Centennial', year: 1978 }, { genres: [ 'Documentary', 'History', 'Sport' ], title: 'Baseball', year: 1994 } ]
Utiliza múltiples condiciones de JOIN y una sub-query correlacionada
Las pipelines pueden ejecutarse en una colección externa e incluir múltiples condiciones de unión. El operador $expr permite condiciones de unión más complejas, incluidas las conjunciones y las coincidencias inexactas.
Una condición de unión puede referirse a un campo en la colección local en la que se ejecutó el método aggregate() y referirse a un campo en la colección extranjera. Esto permite una sub-query correlacionada entre ambas colecciones.
MongoDB 5.0 soporta los subquery correlacionados concisos.
El siguiente ejemplo:
Une las colecciones
moviesycommentsusando los campos_idymovie_id.Filtra los comentarios para incluir únicamente aquellos publicados después del año de estreno de la película.
db.movies.aggregate( [ { $match: { title: { $in: [ "Class Action", "Kafka", "Corpse Bride" ] } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", let: { movie_year: "$year" }, pipeline: [ { $match: { $expr: { $gt: [ { $year: "$date" }, "$$movie_year" ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { year: 1991, title: 'Class Action', post_release_comments: [ { name: 'Khal Drogo', date: ISODate('2016-12-06T07:17:03.000Z') } ] }, { year: 1991, title: 'Kafka', post_release_comments: [ { name: 'Khal Drogo', date: ISODate('1998-05-10T03:10:20.000Z') } ] }, { year: 2005, title: 'Corpse Bride', post_release_comments: [] } ]
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, post_release_comments FROM movies WHERE post_release_comments IN ( SELECT name, date FROM comments WHERE movie_id = movies._id AND YEAR(date) > movies.year );
Los operadores de comparación $eq, $lt, $lte, $gt y $gte colocados en un operador $expr pueden utilizar un índice en la colección from mencionada en una etapa $lookup. Limitaciones:
Los índices solo pueden utilizarse para comparaciones entre campos y constantes, por lo que el operando
letdebe resolverse en una constante.Por ejemplo, una comparación entre
$ay un valor constante puede utilizar un índice, pero una comparación entre$ay$bno puede.Los índices no se utilizan para comparaciones donde el operando
letse resuelve en un valor vacío o faltante.No se utilizan índices multiclave, parciales o dispersos.
Por ejemplo, si el índice { movie_id: 1 } existe en la colección comments:
La coincidencia exacta en el campo
comments.movie_idutiliza el índice.
Realice una sub-query no correlacionada con $lookup
Una etapa de $lookup de una pipeline de agregación puede ejecutar una pipeline en la colección foránea, lo que permite sub-query no correlacionadas. Una sub-query no correlacionada no hace referencia a los campos locales del documento.
Nota
A partir de MongoDB 5.0, para un subquery no correlacionado en una etapa de la pipeline de $lookup que contenga una etapa $sample, el operador $sampleRate o el operador $rand, el subquery siempre se ejecuta nuevamente si se repite. Anteriormente, según el tamaño del resultado del subquery, se almacenaba en caché o el subquery se ejecutaba nuevamente.
La siguiente operación une la colección users con las películas que tienen una duración superior a 1000 minutos, pertenecientes a la colección movies:
db.users.aggregate( [ { $match: { email: { $in: [ "mark_addy@gameofthron.es", "lena_headey@gameofthron.es" ] } } }, { $lookup: { from: "movies", pipeline: [ { $match: { runtime: { $gt: 1000 } } }, { $project: { _id: 0, title: 1, year: 1 } } ], as: "long_movies" } }, { $project: { _id: 0, name: 1, email: 1, long_movies: 1 } } ] )
[ { name: 'Robert Baratheon', email: 'mark_addy@gameofthron.es', long_movies: [ { title: 'Centennial', year: 1978 }, { title: 'Baseball', year: 1994 } ] }, { name: 'Cersei Lannister', email: 'lena_headey@gameofthron.es', long_movies: [ { title: 'Centennial', year: 1978 }, { title: 'Baseball', year: 1994 } ] } ]
La operación corresponde a esta instrucción pseudo-SQL:
SELECT *, long_movies FROM users WHERE long_movies IN ( SELECT title, year FROM movies WHERE runtime > 1000 );
Para obtener más información, consulta Consideraciones sobre el rendimiento de subquery no correlacionado.
Realice una sub-query correlacionada concisa con $lookup
Nuevo en la versión 5.0.
A partir de MongoDB 5.0, una etapa de $lookup de la pipeline de agregación admite una sintaxis concisa de subquery correlacionado que mejora las uniones entre colecciones. La nueva sintaxis concisa remueve el requisito de una coincidencia exacta en los campos externos y locales dentro de un operador $expr en una etapa $match.
El siguiente ejemplo:
Une las colecciones
moviesycommentshaciendo coincidir el localField_idcon el foreignFieldmovie_id. La coincidencia se realiza antes de que se ejecute elpipeline.Filtra los comentarios para incluir solo aquellos publicados después del año de estreno de la película, accediendo respectivamente mediante
$$movie_yeary$date.
db.movies.aggregate( [ { $match: { title: { $in: [ "I Don't Kiss", "Lucky Luke", "Mississippi Masala" ] } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", let: { movie_year: "$year" }, pipeline: [ { $match: { $expr: { $gt: [ { $year: "$date" }, "$$movie_year" ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { title: "I Don't Kiss", year: 1991, post_release_comments: [ { name: 'Brandon Hardy', date: ISODate('2016-09-18T11:11:34.000Z') } ] }, { title: 'Lucky Luke', year: 1991, post_release_comments: [ { name: 'Kelsey Smith', date: ISODate('2010-01-13T17:55:01.000Z') } ] }, { title: 'Mississippi Masala', year: 1991, post_release_comments: [ { name: 'Phillip Collins', date: ISODate('2010-05-13T08:04:22.000Z') } ] } ]
Este ejemplo utiliza la sintaxis detallada de las versiones de MongoDB anteriores a la 5.0 y arroja los mismos resultados que el ejemplo conciso anterior:
db.movies.aggregate( [ { $match: { title: { $in: [ "I Don't Kiss", "Lucky Luke", "Mississippi Masala" ] } } }, { $lookup: { from: "comments", let: { movie_id: "$_id", movie_year: "$year" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: [ "$movie_id", "$$movie_id" ] }, { $gt: [ { $year: "$date" }, "$$movie_year" ] } ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { title: "I Don't Kiss", year: 1991, post_release_comments: [ { name: 'Brandon Hardy', date: ISODate('2016-09-18T11:11:34.000Z') } ] }, { title: 'Lucky Luke', year: 1991, post_release_comments: [ { name: 'Kelsey Smith', date: ISODate('2010-01-13T17:55:01.000Z') } ] }, { title: 'Mississippi Masala', year: 1991, post_release_comments: [ { name: 'Phillip Collins', date: ISODate('2010-05-13T08:04:22.000Z') } ] } ]
Los ejemplos anteriores corresponden a esta instrucción pseudo-SQL:
SELECT *, post_release_comments FROM movies WHERE post_release_comments IN ( SELECT * FROM comments WHERE comments.movie_id = movies._id AND YEAR(comments.date) > movies.year );
Para obtener más información, consulta Consideraciones sobre el rendimiento de los subquery correlacionados.
Los ejemplos de C# en esta página utilizan la base de datos sample_mflix de los conjuntos de datos de muestra de Atlas. Para aprender a crear un clúster gratuito de MongoDB Atlas y cargar los conjuntos de datos de muestra, consulta Primeros pasos en la documentación del controlador de MongoDB .NET/C#.
La siguiente clase Movie modela los documentos en la colección sample_mflix.movies:
public class Movie { public ObjectId Id { get; set; } public int Runtime { get; set; } public string Title { get; set; } public string Rated { get; set; } public List<string> Genres { get; set; } public string Plot { get; set; } public ImdbData Imdb { get; set; } public int Year { get; set; } public int Index { get; set; } public string[] Comments { get; set; } [] public DateTime LastUpdated { get; set; } }
Nota
ConventionPack para Pascal Case
Las clases de C# en esta página utilizan Pascal case para los nombres de sus propiedades, pero los nombres de los campos en la colección de MongoDB utilizan camel case. Para tener en cuenta esta diferencia, se puede usar el siguiente código para registrar un ConventionPack cuando la aplicación se inicie:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
La siguiente clase Comment modela los documentos en la colección sample_mflix.comments:
public class Comment { public Guid Id { get; set; } [] public Guid MovieId { get; set; } public string Text { get; set; } }
Para utilizar el driver MongoDB .NET/C# para añadir una fase $lookup a una pipeline de agregación, llamar al Lookup() método en un objeto PipelineDefinition.
El siguiente ejemplo crea una etapa de pipeline que realiza una unión externa izquierda entre las colecciones movies y comments. El código une el campo Id de cada documento Movie al campo MovieId en los documentos Comment. Los comentarios de cada película se almacenan en un campo llamado Comments en cada documento Movie.
var commentCollection = client .GetDatabase("aggregation_examples") .GetCollection<Comment>("comments"); var pipeline = new EmptyPipelineDefinition<Movie>() .Lookup<Movie, Movie, Comment, Movie>( foreignCollection: commentCollection, localField: m => m.Id, foreignField: c => c.MovieId, @as: m => m.Comments);
Los ejemplos de Node.js en esta página utilizan la base de datos sample_mflix de los conjuntos de datos de muestra de Atlas. Para aprender a crear un clúster gratuito de MongoDB Atlas y cargar los conjuntos de datos de muestra, consulte Primeros pasos en la documentación del controlador de MongoDB Node.js.
Para utilizar el controlador de MongoDB Node.js para agregar una etapa de $lookup a una canalización de agregación, utilice el Operador $lookup en un objeto de canalización.
El siguiente ejemplo crea una etapa de pipeline que realiza una unión externa izquierda entre las colecciones movies y comments. El código une el campo _id de cada documento movie al campo movie_id en los documentos comment. El campo comments almacena los comentarios de cada película en cada documento movie. A continuación, el ejemplo ejecuta la pipeline de agregación:
const pipeline = [ { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "comments" } } ]; const cursor = collection.aggregate(pipeline); return cursor;