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/



