Alisson Machado
20 February 2017

MongoDB Aggregation

Na minha jornada infinita de aprender coisas novas em T.I, nos assuntos DevOps, BigData, DataScience, Clusters, Virtualização e tudo mais. Estou essa semana focado em extrair relatórios do MongoDB para fazer learning analytics pro Moodle, como o MongoDB é um banco de dados noSQL, não existe o relacionamento entre tabelas como somos acostumados em bancos de dados SQL. Sendo assim para fazer buscas mais complexas e unir vários documentos, pode-se usar o Aggregation Framework. Aggregation é uma forma de processar documentos distintos com o objetivo de agrupar em uma única saída, facilitando assim a geração de resultados e performance na hora de efetuar essas buscas. Abaixo vou explicar vários parâmetros de como utilizar esse recurso e elaborar buscas mais específicas. Atenção: Todos os comandos abaixo foram executados na versão 3.4 do MongoDB.


alisson@alisson-pc:~/MoodlePlus$ mongo
MongoDB shell version: 3.2.11
connecting to: test
use exemplo
switched to db exemplo

  No log acima entrei no console do MongoDB e acessei o  banco exemplo, nesse banco vou criar uma collection chamada funcionários e inserir alguns registros:

db.funcionarios.insert({"nome":"Alisson","sobrenome":"Machado de Menezes","cargo":"Analista de Sistemas DevOps", "empresa":"4Linux"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Mariana","sobrenome":"Albano","cargo":"Analista de Sistemas DevOps", "empresa":"4Linux"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Antonio","sobrenome":"Gomes","cargo":"Supervidor de Suporte", "empresa":"4Linux"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Gabriel","sobrenome":"Bonfim","cargo":"Desenvolvedor Senior", "empresa":"4Linux"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Hederson","sobrenome":"Boechat","cargo":"Desenvolvedor Senior", "empresa":"Nadir"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Guilherme","sobrenome":"Chagas","cargo":"Analista de Sistemas DevOps", "empresa":"BankFacil"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Hederson","sobrenome":"Boechat","cargo":"Desenvolvedor Senior", "empresa":"Nadir"})
WriteResult({ "nInserted" : 1 })

db.funcionarios.insert({"nome":"Fernando","sobrenome":"Paiva","cargo":"Analista de B.I", "empresa":"Lemman"})
WriteResult({ "nInserted" : 1 })


Para listar todos os documentos digite a instrução abaixo:

db.funcionarios.find()


{ "_id" : ObjectId("58a7479844f122d1903c551f"), "nome" : "Alisson", "sobrenome" : "Machado de Menezes", "cargo" : "Analista de Sistemas DevOps", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747a144f122d1903c5520"), "nome" : "Mariana", "sobrenome" : "Albano", "cargo" : "Analista de Sistemas DevOps", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747ac44f122d1903c5521"), "nome" : "Antonio", "sobrenome" : "Gomes", "cargo" : "Supervidor de Suporte", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747b644f122d1903c5522"), "nome" : "Gabriel", "sobrenome" : "Bonfim", "cargo" : "Desenvolvedor Senior", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747c044f122d1903c5523"), "nome" : "Hederson", "sobrenome" : "Boechat", "cargo" : "Desenvolvedor Senior", "empresa" : "Nadir" }
{ "_id" : ObjectId("58a747c844f122d1903c5524"), "nome" : "Guilherme", "sobrenome" : "Chagas", "cargo" : "Analista de Sistemas DevOps", "empresa" : "BankFacil" }
{ "_id" : ObjectId("58a747d144f122d1903c5525"), "nome" : "Hederson", "sobrenome" : "Boechat", "cargo" : "Desenvolvedor Senior", "empresa" : "Nadir" }
{ "_id" : ObjectId("58a747da44f122d1903c5526"), "nome" : "Fernando", "sobrenome" : "Paiva", "cargo" : "Analista de B.I", "empresa" : "Lemman" }

Agora que a base dados já está populada é possível utilizar do aggregate para filtrar essas buscas. Por exemplo, se eu quiser buscar todos os funcionários que trabalhando na empresa 4linux:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}}])
{ "_id" : ObjectId("58a7479844f122d1903c551f"), "nome" : "Alisson", "sobrenome" : "Machado de Menezes", "cargo" : "Analista de Sistemas DevOps", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747a144f122d1903c5520"), "nome" : "Mariana", "sobrenome" : "Albano", "cargo" : "Analista de Sistemas DevOps", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747ac44f122d1903c5521"), "nome" : "Antonio", "sobrenome" : "Gomes", "cargo" : "Supervidor de Suporte", "empresa" : "4Linux" }
{ "_id" : ObjectId("58a747b644f122d1903c5522"), "nome" : "Gabriel", "sobrenome" : "Bonfim", "cargo" : "Desenvolvedor Senior", "empresa" : "4Linux" }
> 

Caso queira agrupar os valores, é possível utilizando a instrução $group, essa instrução obrigatoriamente precisa que seja passado um _id e esse ai deve ser um campo do documento retornado. Por exemplo:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$group":{"_id":"$nome"}}])
{ "_id" : "Gabriel" }
{ "_id" : "Mariana" }
{ "_id" : "Antonio" }
{ "_id" : "Alisson" }

Para ver o total de documentos agrupados, pode-se utilizar a instrução $sum:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$group":{"_id":"$empresa","total":{"$sum":1}}}])
{ "_id" : "4Linux", "total" : 4 }

Para contar todos os resultados sem o agrupamento, pode-se utilizar a instrução $count, essa instrução só existe a partir da versão 3.4:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$count":"total"}])

É possível também projetar uma saída, ou seja, modelar o documento e mostrar apenas campos específicos aumentando a performance da sua busca e trazendo resultados mais fáceis de se encontrar. Por exemplo:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$project":{"nome":1,"cargo":1}}])
{ "_id" : ObjectId("58a7479844f122d1903c551f"), "nome" : "Alisson", "cargo" : "Analista de Sistemas DevOps" }
{ "_id" : ObjectId("58a747a144f122d1903c5520"), "nome" : "Mariana", "cargo" : "Analista de Sistemas DevOps" }
{ "_id" : ObjectId("58a747ac44f122d1903c5521"), "nome" : "Antonio", "cargo" : "Supervidor de Suporte" }
{ "_id" : ObjectId("58a747b644f122d1903c5522"), "nome" : "Gabriel", "cargo" : "Desenvolvedor Senior" }

O exemplo acima, pode-se ver que foi foram mostrados somente os campos _id, nome e cargo, sendo assim quando se quer mostrar um campo, coloca-se o valor 1 em conjunto com a chave, caso não queira pode se colocar o valor 0. Por exemplo:

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$project":{"nome":1,"cargo":1,"_id":0}}])
{ "nome" : "Alisson", "cargo" : "Analista de Sistemas DevOps" }
{ "nome" : "Mariana", "cargo" : "Analista de Sistemas DevOps" }
{ "nome" : "Antonio", "cargo" : "Supervidor de Suporte" }
{ "nome" : "Gabriel", "cargo" : "Desenvolvedor Senior" }

No exemplo acima foi ocultado o campo _id para melhorar a visualização do documento. O Aggregation do MongoDB trabalha com um esquema de pipeline, ou seja, você pode concatenar todas as instruções juntas. Por exemplo: Pode-se fazer um $match, depois um $project para limitar os campos e depois um $group para agrupar pelos cargo e saber quantas pessoas daquela posição existem na empresa.

> db.funcionarios.aggregate([{"$match":{"empresa":"4Linux"}},{"$project":{"nome":1,"cargo":1,"_id":0}},{"$group":{"_id":"$cargo","total":{"$sum":1}}}])
{ "_id" : "Desenvolvedor Senior", "total" : 1 }
{ "_id" : "Supervidor de Suporte", "total" : 1 }
{ "_id" : "Analista de Sistemas DevOps", "total" : 2 }

Esses são os comandos básicos para fazer um Aggregate no MongoDB. Qualquer dúvida é só me dar um salve.