Durante as minhas palestras sobre MongoDB, é muito comum a dúvida sobre joins – como fazer “relacionamento” em bancos não relacionais e como 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 nova versão 3.2
Na versão 3.2 do MongoDB (estável), foi disponibilizado 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 fim, 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.
***
Artigo baseado no post Suporte a Joins no MongoDB (operador $lookup) publicado originalmente em http://christiano.me/mongodb-com-suporte-a-joins/