Definição
$lookupAlterado na versão 5.1.
Executa uma junção externa esquerda em uma coleção no mesmo banco de dados para filtrar documentos da coleção estrangeira para processamento. O estágio
$lookupadiciona um novo campo de matriz para cada documento de entrada. O novo campo de array contém os documentos correspondentes da coleção estrangeira. O estágio$lookuppassa esses documentos remodelados para o próximo estágio.A partir do MongoDB 5.1, é possível usar
$lookupcom coleções fragmentadas.Para combinar elementos de duas coleções diferentes, use o estágio de pipeline
$unionWith.Importante
O uso excessivo de
$lookuppode diminuir o desempenho da query. Para reduzir a dependência de$lookup, considere um modelo de dados incorporado para armazenar dados relacionados em uma única coleção.Para obter detalhes sobre o desempenho
$lookup, consulte Considerações sobre desempenho.
Compatibilidade
Você pode utilizar o $lookup para implantações hospedadas nos seguintes ambientes:
MongoDB Atlas: o serviço totalmente gerenciado para implantações do MongoDB na nuvem
MongoDB Enterprise: a versão autogerenciada e baseada em assinatura do MongoDB
MongoDB Community: uma versão com código disponível, de uso gratuito e autogerenciada do MongoDB
Sintaxe
A sintaxe de estágio $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> } }
O $lookup aceita um documento com estes campos:
Campo | necessidade | Descrição |
|---|---|---|
Obrigatório | Especifica a coleção estrangeira no mesmo banco de dados para ingressar na coleção local. É possível, em alguns casos extremos, substituir A partir do MongoDB 5.1, a coleção | |
Opcional se | Especifica o campo da entrada de documentos para o | |
Opcional se | Especifica o Se um documento externo não contiver um | |
Opcional | Especifica variáveis a serem usadas nos estágios do pipeline. Use as expressões variáveis para acessar os campos dos documentos da coleção local que são inseridos no Para fazer referência a variáveis em estágios do pipeline, use a As variáveis let podem ser acessadas pelos estágios no pipeline, incluindo os estágios adicionais
| |
Opcional se | Especifica o O O Para fazer referência a variáveis em estágios do pipeline, use a As variáveis let podem ser acessadas pelos estágios no pipeline, incluindo os estágios adicionais
| |
Obrigatório | Especifica o nome do novo campo de array a ser adicionado aos documentos de entrada. O novo campo de array contém os documentos correspondentes da coleção |
Correspondência de igualdade com uma única condição de junção
Para realizar uma correspondência de igualdade entre um campo dos documentos de entrada e um campo dos documentos da coleção estrangeira, a etapa $lookup tem a seguinte sintaxe:
{ $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> } }
Observação
Neste exemplo, pipeline é opcional e executado após o estágio de igualdade local e externa.
A operação corresponde a esta declaração pseudo-SQL:
SELECT *, ( SELECT ARRAY_AGG(*) FROM <collection to join> WHERE <foreignField> = <collection.localField> ) AS <output array field> FROM collection;
Observação
As declarações SQL nesta página estão incluídas para comparação com a sintaxe do pipeline de agregação do MongoDB. As declarações SQL não são executáveis.
Para exemplos do MongoDB, consulte estas páginas:
Condições de junção e subqueries em uma coleção estrangeira
MongoDB suporta:
Executando um pipeline em uma coleção estrangeira.
Várias condições de junções.
Subconsultas correlacionadas e não correlacionadas.
No MongoDB, uma subquery não correlacionada significa que todo documento de entrada retornará o mesmo resultado. Uma subquery correlacionada é um pipeline em um estágio$lookup que usa os campos da collection local ou input para retornar resultados correlacionados a cada documento recebido.
Observação
A partir do MongoDB 5.0, para uma sub-query não correlacionada em um estágio de pipeline $lookup que contém um estágio $sample, o operador $sampleRate ou o operador $rand, a sub-query é sempre executada novamente se for repetida. Anteriormente, dependendo do tamanho da saída da sub-query, o resultado da sub-query era armazenado em cache ou a sub-query era executada novamente.
As subconsultas correlacionadas do MongoDB são comparáveis às subconsultas correlacionadas do SQL, onde a consulta interna faz referência a valores de consulta externa. Uma subconsulta SQL não correlacionada não faz referência a valores de consulta externa.
O MongoDB 5.0 também suporta subqueries correlacionadas concisas.
Para executar subconsultas correlacionadas e não correlacionadas com duas coleções e executar outras condições de junção além de uma única correspondência de igualdade, use esta sintaxe $lookup:
{ $lookup: { from: <foreign collection>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run on foreign collection> ], as: <output array field> } }
A operação corresponde a esta declaração 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> );
Veja os seguintes exemplos:
Subconsultas correlacionadas usando sintaxe concisa
Novidades na versão 5.0.
A partir do MongoDB 5.0, você pode usar uma sintaxe concisa para uma subquery correlacionada. As subqueries correlacionadas fazem referência a campos de documentos de uma coleção estrangeira e da coleção "local" na qual o método aggregate() foi executado.
A nova sintaxe concisa a seguir remove o requisito de uma correspondência de igualdade nos campos estrangeiros e local dentro de um 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> } }
A operação corresponde a esta declaração 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> );
Veja este exemplo:
Comportamento
Visualizações e agrupamento
Se executar uma agregação que envolva múltiplas visualizações, como com $lookup ou $graphLookup, as visualizações deverão ter o mesmo agrupamento.
Restrições
Você não pode incluir os estágios $out ou $merge no estágio $lookup. Ou seja, ao especificar um pipeline para a coleção estrangeira, você não pode incluir nenhum dos estágios no 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> } }
Suporte do Atlas Search
A partir do MongoDB 6.0, você pode especificar o $search do Atlas Search ou o estágio do $searchMeta no pipeline de $lookup para pesquisar coleções no cluster do Atlas. O estágio $search ou $searchMeta deve ser o primeiro dentro do pipeline $lookup.
Por exemplo, ao realizar Condições de junção e subqueries em uma coleção estrangeira ou executar Subqueries correlacionadas usando sintaxe concisa, você pode especificar $search ou $searchMeta dentro do pipeline, conforme mostrado abaixo:
Para ver um exemplo de $lookup com $search, consulte o tutorial Executar uma query $search do Atlas Search usando $lookup, do Atlas Search.
Coleções fragmentadas
A partir do MongoDB 5.1, você pode especificar coleções fragmentadas no parâmetro from de $lookup estágios.
Você não pode usar a etapa $lookup dentro de uma transação enquanto segmenta uma coleção fragmentada.
Mecanismo de execução de consulta baseado em slot
Observação
A partir da versão 7.0.17, o mecanismo de execução de queries baseado em slots não está mais ativado por padrão para versões de patch do 7.0. Se você deseja que suas queries usem o mecanismo de execução de queries baseado em slots, atualize para a versão 8.0, onde está habilitado por padrão.
A partir da versão 6.0, o MongoDB pode usar o mecanismo de execução de query baseado em slots para executar $lookup estágios se todos os estágios anteriores no pipeline também puderem ser executados pelo mecanismo de execução baseado em slots e nenhuma das seguintes condições for verdadeira:
A operação
$lookupexecuta um pipeline em uma coleção estrangeira. Para ver um exemplo desse tipo de operação, consulte Condições de união e subqueries em uma coleção estrangeira.Os
localFieldouforeignFieldde$lookupespecificam componentes numéricos. Por exemplo:{ localField: "restaurant.0.review" }.O campo
fromde qualquer$lookupno pipeline especifica uma visualização ou coleção fragmentada.
Para mais informações, consulte Otimização do$lookup.
Considerações de desempenho
$lookup o desempenho depende do tipo de operação executada. Consulte a tabela a seguir para considerações de desempenho para diferentes operações do $lookup.
$lookup (operação) | Considerações de desempenho |
|---|---|
| |
| |
|
Para obter estratégias gerais de desempenho, consulte Estratégias de indexação e Otimização de consulta.
Exemplos
Os exemplos nesta página usam dados do conjunto de dados de amostra sample_mflix. Para obter detalhes sobre como carregar esse conjunto de dados em sua implantação autogerenciada do MongoDB , consulte Carregar o conjunto de dados de amostra. Se você fez modificações nos bancos de dados de amostra, talvez seja necessário descartar e recriar os bancos de dados para executar os exemplos nesta página.
Realizar uma única junção de igualdade com $lookup
A operação de agregação a seguir primeiro filtra a coleção movies para filmes com runtime maior que 1000 e, em seguida, une-se à coleção comments nos campos _id e 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: [] } ]
A operação corresponde a esta declaração pseudo-SQL:
SELECT *, movie_comments FROM movies WHERE movie_comments IN ( SELECT * FROM comments WHERE movie_id = movies._id );
Para obter mais informações, consulte Considerações sobre o desempenho da correspondência de igualdade.
Use $lookup com um Array
Se localField for uma array, você poderá fazer a correspondência dos elementos de array com um escalar foreignField sem um estágio $unwind.
A seguinte operação de agregação une a coleção movies à coleção users, fazendo a correspondência do campo de array cast de movies com o 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' } ] } ]
Use $lookup com $mergeObjects
O operador $mergeObjects combina vários documentos em um único documento.
A operação a seguir usa $lookup para unir a coleção movies com a coleção comments e, em seguida, usa $mergeObjects em $replaceRoot para mesclar o primeiro document de comentário com o document de filme :
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 } ]
Use várias condições de junção e uma subquery correlacionada
Os pipelines podem ser executados em uma coleção estrangeira e incluir diversas condições de junção. O operador $expr permite condições de junção mais complexas, incluindo conjunções e correspondências de não igualdade.
Uma condição de junção pode fazer referência a um campo na coleção local na qual o método aggregate() foi executado e fazer referência a um campo na coleção estrangeira. Isso permite uma subquery correlacionada entre as duas coleções.
O MongoDB 5.0 é compatível com subconsultas correlacionadas concisas.
O seguinte exemplo:
Une as coleções
moviesecommentsusando os campos_idemovie_id.Filtra os comentários para incluir apenas os postados após o ano de lançamento do filme.
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: [] } ]
A operação corresponde a esta declaração 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 );
Os operadores de comparação $eq, $lt, $lte, $gt e $gte colocados em um operador $expr podem utilizar um índice na coleção from referenciada em um estágio $lookup. Limitações:
Os índices só podem ser usados para comparações entre campos e constantes, portanto, o operando
letdeve ser resolvido para uma constante.Por exemplo, uma comparação entre
$ae um valor constante pode usar um índice, mas uma comparação entre$ae$bnão pode.Os índices não são usados para comparações onde o operando
letresolve para um valor vazio ou ausente.Índices multichave, parciais ou esparsos não são usados.
Por exemplo, se o índice { movie_id: 1 } existir na coleção comments:
A correspondência de igualdade no campo
comments.movie_idutiliza o índice.
Execute uma subconsulta não correlacionada $lookup
Um estágio $lookup do pipeline de agregação pode executar um pipeline na coleção estrangeira, o que permite subqueries não correlacionadas. Uma subquery não correlacionada não faz referência aos campos do documento local.
Observação
A partir do MongoDB 5.0, para uma sub-query não correlacionada em um estágio de pipeline $lookup que contém um estágio $sample, o operador $sampleRate ou o operador $rand, a sub-query é sempre executada novamente se for repetida. Anteriormente, dependendo do tamanho da saída da sub-query, o resultado da sub-query era armazenado em cache ou a sub-query era executada novamente.
A operação a seguir une a coleção users a filmes que têm um tempo de execução maior que 1000 minutos da coleção 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 } ] } ]
A operação corresponde a esta declaração pseudo-SQL:
SELECT *, long_movies FROM users WHERE long_movies IN ( SELECT title, year FROM movies WHERE runtime > 1000 );
Para obter mais informações, consulte Considerações sobre desempenho de subconsultas não correlacionadas.
Execute uma subconsulta concisa correlacionada com $lookup
Novidades na versão 5.0.
A partir do MongoDB 5.0, um estágio $lookup do pipeline de agregação suporta uma sintaxe de sub-query correlacionada concisa que melhora as uniões entre coleções. A nova sintaxe concisa remove a exigência de uma correspondência de igualdade nos campos externo e local dentro de um operador $expr em um estágio $match.
O seguinte exemplo:
Une as coleções
moviesecommentscombinando o localField_idcom o foreignFieldmovie_id. A correspondência é executada antes depipelineser executado.Filtra os comentários para incluir apenas os postados após o ano de lançamento do filme, acessado usando
$$movie_yeare$date, respectivamente.
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 exemplo usa a sintaxe detalhada mais antiga das versões do MongoDB anteriores à 5.0 e retorna os mesmos resultados do exemplo 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') } ] } ]
Os exemplos anteriores correspondem a esta declaração 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 mais informações, consulte Considerações sobre o desempenho de subconsultas correlacionadas.
Os exemplos de C# nesta página utilizam o banco de dados sample_mflix a partir dos conjuntos de dados de amostra do Atlas. Para saber como criar um cluster MongoDB Atlas gratuito e carregar os conjuntos de dados de exemplo, consulte Introdução na documentação do driver MongoDB .NET/C#.
A seguinte classe Movie modela os documentos na collection 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; } }
Observação
ConventionPack para Pascal Case
As classes C# nesta página usam Pascal case para seus nomes de propriedade, mas os nomes de campo na coleção MongoDB usam Camel case. Para considerar essa diferença, você pode usar o seguinte código para registrar um ConventionPack quando o aplicativo iniciar:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
A seguinte classe Comment modela os documentos na collection sample_mflix.comments:
public class Comment { public Guid Id { get; set; } [] public Guid MovieId { get; set; } public string Text { get; set; } }
Para usar o driver MongoDB .NET/C# para adicionar um estágio $lookup a um pipeline de agregação, chame o método Lookup() em um objeto PipelineDefinition.
O exemplo abaixo cria um estágio de pipeline que executa uma junção externa esquerda entre as collections movies e comments. O código une o campo Id de cada documento Movie ao campo MovieId nos documentos Comment. Os comentários de cada filme são armazenados em um campo chamado Comments em 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);
Os exemplos do Node.js nesta página utilizam o banco de dados do sample_mflix a partir dos conjuntos de dados de amostra do Atlas. Para saber como criar um cluster gratuito do MongoDB Atlas e carregar os conjuntos de dados de exemplo, consulte Introdução na documentação do driver do MongoDB Node.js
Para usar o driver Node.js do MongoDB para adicionar um estágio $lookup a um pipeline de agregação , use o operador $lookup em um objeto de pipeline.
O exemplo a seguir cria um estágio de pipeline que executa uma junção externa esquerda entre as coleções movies e comments. O código conecta o campo _id de cada documento movie ao campo movie_id nos documentos comment. O campo comments armazena os comentários de cada filme em cada documento movie. O exemplo em seguida executa o pipeline de agregação:
const pipeline = [ { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "comments" } } ]; const cursor = collection.aggregate(pipeline); return cursor;