Durante as minhas palestras sobre MongoDB, é muito comum a dúvida sobre joins — como fazer “relacionamento” em bancos não relacionais — e juntar informações de duas coleções diferentes.
É evidente que o modelo não relacional traz muita flexibilidade, para isso é necessário abrir mão de alguns recursos (como por exemplo integridade referencial) para ganhar outros recursos, como schema flexível. Até então era possível fazer algo semelhante a join via código, basta efetuar uma consulta simples e via código buscar a referência em outras coleções.
Mudanças na versão 3.2
O MongoDB 3.2 (no momento está em release candidate, mas deve ser lançado nos próximos dias) implementou o operador de agregação $lookup que nada mais é do que o left join do SQL. Esse operador realiza a busca em duas coleções diferentes e agrega os documentos que possuem o mesmo identificador. Siga o exemplo abaixo:
Uma aplicação de e-commerce possui duas coleções: produtos e pedidos.
A coleção de produtos possui o seguinte formato:
{ "_id" : 1, "titulo" : "Moto X 2 Geração", "fabricante" : "Motorola", "preco" : 1099.99 } { "_id" : 2, "titulo" : "Capinha para Moto X 2 Geração", "fabricante" : "Xing Ling", "preco" : 29.9 } { "_id" : 3, "titulo" : "Carregador Power Turbo", "fabricante" : "Power Turbo", "preco" : 199 }
E a coleção de pedidos possui o seguinte formato:
{ "_id" : 1, "usuario" : "Christiano Anderson", "produto_id" : 1, "quantidade" : 1 } { "_id" : 2, "usuario" : "Ivan", "produto_id" : 1, "quantidade" : 1 } { "_id" : 3, "usuario" : "Carol", "produto_id" : 2, "quantidade" : 3 } { "_id" : 4, "usuario" : "Juliana", "produto_id" : 3, "quantidade" : 1 } { "_id" : 5, "usuario" : "Luiz", "produto_id" : 3, "quantidade" : 1 }
Reparem que na coleção de pedidos, a chave produto_id indica qual o é o _id da coleção de produtos.
Usando o novo operador $lookup, a query de agregação ficaria da seguinte forma:
db.pedidos.aggregate([ { $lookup: { from: "produtos", localField: "produto_id", foreignField: "_id", as: "pedidos_realizados" } } ])
A agregação é realizada na coleção de pedidos, na linha 5 (from) é necessário indicar em qual coleção a busca deve ser realizada, na linha 6 qual é a chave de busca, na linha 7 é indicada qual a chave que deverá ser encontrada (foreignField) e por final, na linha 8, um nome para o subdocumento que receberá esse cruzamento. O resultado fica assim:
{ "_id" : 1, "usuario" : "Christiano Anderson", "produto_id" : 1, "quantidade" : 1, "pedidos_realizados" : [ { "_id" : 1, "titulo" : "Moto X 2 Geração", "fabricante" : "Motorola", "preco" : 1099.99 } ] } { "_id" : 2, "usuario" : "Ivan", "produto_id" : 1, "quantidade" : 1, "pedidos_realizados" : [ { "_id" : 1, "titulo" : "Moto X 2 Geração", "fabricante" : "Motorola", "preco" : 1099.99 } ] } { "_id" : 3, "usuario" : "Carol", "produto_id" : 2, "quantidade" : 3, "pedidos_realizados" : [ { "_id" : 2, "titulo" : "Capinha para Moto X 2 Geração", "fabricante" : "Xing Ling", "preco" : 29.9 } ] } { "_id" : 4, "usuario" : "Juliana", "produto_id" : 3, "quantidade" : 1, "pedidos_realizados" : [ { "_id" : 3, "titulo" : "Carregador Power Turbo", "fabricante" : "Power Turbo", "preco" : 199 } ] } { "_id" : 5, "usuario" : "Luiz", "produto_id" : 3, "quantidade" : 1, "pedidos_realizados" : [ { "_id" : 3, "titulo" : "Carregador Power Turbo", "fabricante" : "Power Turbo", "preco" : 199 } ] }
No resultado o operador exibe todos os documentos da coleção incluindo um subdocumento (no exemplo acima está nomeado como “pedidos_realizados“) com o resultado do join.
É possível também combinar a agregação com outros elementos como $project, $match e outros disponíveis no aggregation framework. Basta acertar a sequência do pipeline que o aggregation realiza tarefas bem avançadas.
Nota: a versão 3.2 está em release candidate no momento em que escrevo esse post. Não use ainda em produção! A versão final deve ser lançada nos próximos dias. Atualização: a versão 3.2 já está disponível como estável, pronta para uso em produção!
Se você gostou dessa dica, curta, deixe um comentário e compartilhe nas redes sociais. 🙂