Data

24 fev, 2016

MongoDB com suporte a Joins

Publicidade

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/