No MongoDb podemos utilizar o Aggregation Framework para agruparmos valores para alguma finalidade, por exemplo fazer uma query que retorne a média salarial dos seus funcionários.
Um agrupamento pode ser feito com um operador aritimético:
- Valor absoluto
$abs
- Adição
$add
- Arredondamento "para baixo"
$ceil
- Arredondamento "para cima"
$floor
- Truncar para inteiro
$trunc
sem arredondar - dentre outros
Com um acumulador:
- Soma
$sum
- Média
$avg
- Mínimo
$min
- Máximo
$max
- Desvio padrão
$stdDevPop
- dentre outros
Com strings
- Concatenação
$concat
- Retornar parte de uma string
$substr
- Converter para minúsculas
$toLower
- Converter para maiúsculas
$toUpper
- Comparar
$strcasecmp
Com arrays:
- Junta arrays e retorna um novo com todos os elementos
$concatArrays
- Retornar o número de elementos de um array
$size
- Reparte um array, retirando um pedaço dele
$slice
- dentre outros
Com datas:
- Dia do ano de uma data (de 1 a 366)
$dayOfYear
- Número de milisegundos (de 0 a 999)
$millisecond
- dentre outros
Vamos dar uma olhada na sintaxe geral do método aggregate:
db.collection.aggregate(pipeline, options)
Sendo estas abaixo todas as instruções do pipeline:
[
{ $project: <RETORNA UM DOCUMENTO ADICIONANDO OU REMOVENDO CAMPOS PARA A STREAM DO PIPELINE> },
{ $match: <CONDIÇÃO DE FILTRO> },
{ $redact: <RETORNA O DOCUMENTO PARA A STREAM DO PIPELINE> },
{ $limit: <LIMITA A QUANTIDADE DE DOCUMENTOS A SEREM CONSIDERADOS> },
{ $skip: <PULA OS PRIMEIROS n DOCUMENTOS> },
{ $unwind <DESCONTROI UM ARRAY EM UM DOCUMENTO> },
{ $group: { _id: <KEY AGRUPADORA>, <OUTROS CAMPOS A SEREM AGRUPADOS> },
{ $sample: <AMOSTRA ALEATÓRIA> },
{ $sort: <ORDENAÇÃO A SER CONSIDERADA> },
{ $geoNear: <BUSCA UTILIZANDO LATITUDE E LONGITUDE> },
{ $lookup: <REALIZA UM 'left outer join' COM OUTRA COLEÇÃO DO MESMO BANCO DE DADOS> },
{ $out: <ESCREVE O RESULTADO EM UMA COLLECTION> },
{ $indexStats: <INDICA SE ALGUM ÍNDICE FOI UTILIZADO> }
]
Como o próprio nome pipeline
indica, a order das declarações importa e modifica o resultado do aggregate. Então, se você quer por exemplo, ordenar o retorno, deixe o parâmetro sort
por último, e não antes do group
, entendeu?
Não vamos utilizar todas as opções disponíveis de uma só vez, e podemos fazer qualquer combinação delas, enquanto estamos construindo a nossa agregação, para atingirmos o resultado esperado.
E os options
(argumento opcional que não precisa ser enviado):
{
explain: boolean,
allowDiskUse: boolean,
cursor: boolean,
bypassDocumentValidation: boolean,
readConcern: boolean
}
Basicamente, agregações mais simples podem ser feitas utilizando o método .group()
, com uma sintaxe bem diferente, e um pouco mais manualmente, já que no group, recorremos a estratégia de map/reduce, em vez de termos operadores prontos que fazem o trabalho de média por exemplo para nós.
A agregação pipeline consiste em etapas, ou seja, cada estágio transforma os documentos a medida que passa através do pipeline.
A melhor definição de Pipeline é uma unidade de processamento de dados.
Temos a seguinte leitura na imagem acima:
Obtemos os documentos da nossa coleção e em seguida temos as fases stages
de cada um dos quais realiza a operação de cada entrada. Para cada fase temos uma modificação na nossa saída dos documentos. É importante planejar cada fase para que possamos realmente obter os documentos desejados.
Explicarei melhor com exemplos, logo em seguida e para entendermos sobre o que podemos ter em cada stages e facilitar nosso dia a dia.
Como estou fazendo o curso Be-mean, usarei o banco be-mean e a coleção pokemons para prosseguir com os exemplos. Então para uma melhor aprendizagem sugiro seguir esses passos abaixo:
- Importar o arquivo json aqui para o banco be-mean. Puts, mas eu não sei como vou fazer para importar esses arquivos para o meu mongodb. Segue esse vídeo em 2:03 minutos de video o professor William Bruno pode te ajudar, é lógico, se você estiver com o mongodb instalado. Caso ainda não instalou siga essa documentação para instalar em seu S.O.
Como estou vindo do banco relacional, primeiramente vale lembrar de como o banco de dados relacional trabalha em relação a ordem lógica de execução.
Mas o que tem a ver a ordem lógica de execução do banco relacional com aggregation
do mongodb?
Muita calma nessa hora, o que eu quero mostrar é a ordem da estrutura no mongodb, onde dependendo do posicionamento dos operadores $limit
, $skip
e $sort
o resultado pode não ser o esperado.
Ordem lógica de execução
Na imagem acima, temos a ordem lógica de execução em um banco de dados SQL SERVER. Vale lembrar que esse é o modelo padrão da estrutura sql que utilizo normalmente no relacional. Temos a mesma estrutura em outros bancos como MySql, Postgres, DB2 e etc.
ou operadores de agregação
Iremos conhecer os operadores que podem fazer parte do método aggregate
.
Filtra os documentos que correspondem à condição especificada, para seguir no stages
do pipeline seguinte. O $match
nada mais é que o método find().
exemplo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}}
])
Estamos utilizando o método aggregate e realizando um find
com o operador $match
, onde irá retornar da nossa coleção pokemons, todos pokemons onde o speed
é maior ou igual 40.
Se quisermos informar os campos que queremos na nossa saida dos dados output
. Vale ressaltar que estamos adicionando mais uma etapa na nossa stages
.
exemplo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$project: {
_id: 0,
name: 1,
speed: 1
}}
])
Nesse caso o _id
não será apresentado em nossa saída, apenas os nomes dos pokemons
e speed
utilizando o método de agregação.
Limita o número de documentos passados para a próxima etapa no pipeline.
exemplo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$limit: 5},
{$project: {
_id: 0,
name: 1
}}
])
Obtemos a quantidade de 5 documentos no processo de cada stages
no pipeline. Apresentando na saída apenas os nomes dos pokemons.
Podemos perceber que podemos trocar a ordem de cada stages
, como o exemplo abaixo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$project: {
_id: 0,
name: 1
}},
{$limit: 5}
])
Temos o mesmos documentos do exemplo anterior. Mas cuidado dependendo dos operadores utilizados os resultados não serão os mesmos.
Ordena os documentos de acordo com a classificação de entrada. onde:
- 1 para especificar ordem crescente.
- -1 para especificar ordem decrescente.
exemplo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$sort: {name: 1, speed: -1}},
{$limit: 5},
{$project: {
_id: 0,
name: 1
}}
])
Nesse caso estamos ordenando os documentos, onde name
em ordem crescente e speed
em ordem decrescente.
No exemplo abaixo iremos trocar o $sort no lugar do $limit. Podemos perceber que os documentos não são os mesmos.
ATENÇÃO!!!, nem sempre os valores que queremos é conforme a ordem que aplicamos no método aggregate. Fiquem ligados na ordem de cada stages
.
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$limit: 5},
{$sort: {name: 1, speed: -1}},
{$project: {
_id: 0,
name: 1
}}
])
Ignora o número especificado de documentos que passam para o stages
e passa os documentos restantes para a próxima fase do pipeline
.
exemplo:
db.pokemons.aggregate([
{$match: {speed: {$gte: 40}}},
{$sort: {name: 1, speed: -1}},
{$skip: 5},
{$limit: 5},
{$project: {
_id: 0,
name: 1
}}
])
Adicionamos mais um operador em nossa stages
para ignorar o número de documentos especificados no método aggregate
. Nesse caso o $skip
ignorou os 5 primeros documentos no processo do pipeline. Perceba que temos cinco stages
para o filtro na nossa coleção pokemons, $match === find()
, $sort
, $skip
, $limit
, e $project
.
##$group É o agrupamento de documentos por alguma expressão especificada e saída para a próxima fase de um documento para cada grupo distinto.
exemplo:
db.pokemons.aggregate(
[
{
$group : {
_id : null,
totalPokemons: {$sum: 1}
}
}
]
)
Apenas para exemplificar o uso do $group
no método aggregate.
A imagem abaixo temos um melhor entendimento no que se refere ao método aggregate.
##Relacional vs. mongodb
Percebam que temos no relacional uma estrutura onde não podemos trocar de lugares conforme as stages
da estrutura do mongodb.
Fora que no mongoDB podemos trabalhar com Embedded Documents de One-to-Many ou One-to-One.
No banco relacional precisaríamos usar JOIN entre várias tabelas.
##Performace Pessoal, em outro post falarei sobre a criação de INDEX no mongodb, para obter uma melhor performance ao utilizar uma busca.
É isso aí e até a próxima!!!!
##Referências 1 - Documentação MongoDB