Agreggation no MongoDB com Mongoose e MongoChef

Gustavo Gondim
Engenharia NOALVO
Published in
4 min readJun 2, 2016

Isso é para VOCÊ que usa o mongoose como ODM no Node.js e sempre sofre quando precisa fazer aquela QUERY COM AGRUPAMENTO.

Eis aqui um bom exemplo de como passei por esse vale das sombras.

Meu cenário:

Obter os menores valores de Precificação de cada Formato.

As models:

var FormatoSchema = new Schema({
nome: String,
});
var PrecificacaoSchema = new Schema({
nome: String,
valor: Number,
formato: {
type: Schema.ObjectId,
ref: 'Formato'
}
});

E o resultado desejado:

[{
_id: idDoFormato,
precificacao: nomeDaMenorPrecificacao,
valorMinimo: valorDaMenorPrecificacao
},
...
]

Entendendendo o aggregate no MongoDB de forma nativa

Em primeiro lugar, foi difícil encontrar COMO fazer isso com uma query nativa no MongoDB, já que sou bem júnior nos paranauês das queries avançadas.

Mas, fuçando a referência oficial de aggregation pipelines, acabei entendendo que deveria fazer esse agrupamento em DIFERENTES PASSOS, o que é chamado de aggregation pipeline.

No meu caso, meu pipeline consiste em três passos:

  1. Agrupar os campos desejados (ID do formato, ID da precificação, nome da precificação e valor da precificação)
  2. Ordenar o aggregate pelo campo desejado (valor)
  3. Agrupar novamente a query pelos campos desejados (ID do formato, nome da menor precificação e valor da menor precificação)

Esse seria o meu pipeline se fizesse de forma nativa no MongoDB:

db.precificacoes.aggregate([
{ $group: {
_id: {
formato: “$formato”,
precificacao: “$_id”,
nome: “$nome”,
valor: "$valor"
}
},
{ $sort: { valor: 1 } },
{ $group: {
_id: “$_id.formato”,
precificacao: { $first: “$_id.nome” },
valorMinimo: { $first: “$valor” } }
}
]);

Criando esse pipeline no MongoChef

1. Clique no botão “Aggregate” na barra de tarefas

2. Clique no menu “Add New Stage”

3. Selecione o operador de aggregation desejado

4. Escreva a expressão do seu operador

Sim, digitar a sua expressão pode parecer um pouco manual ainda no MongoChef, mas ele também não é o Google pra advinhar o que você quer agrupar!

Botões de preview de input (F6) e output (F7)

Mesmo assim, ele conta com ótimos recursos para você otimizar cada passo do pipeline, como os botões de input e output (as setinhas verdes curvadas, cujos atalhos são F6 e F7, respectivamente). Esses botões trazem um preview dos resultados que serão inputados no passo atual e dos resultados que serão gerados para o próximo passo.

Além disso, o MongoChef conta também com ótimos recursos de autocomplete para os operadores e para as propriedades dos documentos que você está trabalhando.

5. Repita o passo 3 para os próximos passos do seu pipeline.

Uma função bem interessante na hora de construir o pipeline é a reorganização de passos do MongoChef. Você pode reorganizá-los utilizando os comandos na barra de tarefas (setinhas verdes para cima e para baixo).

6. Clique no botão “Execute full pipeline (F5)” para ver os resultados do seu pipeline.

7. Clique com o botão direito na listagem do pipeline e depois no menu “Show Query Preview” para obter o script da query.

No meu caso, ficou idêntica à query que eu precisava.

Construindo o pipeline de aggregate no Mongoose

Para construir essa query de aggregate no MongoDB, você usará o método .aggregate(). Existem duas formas de se fazer isso no Mongoose.

  1. Colando a query construída no MongoChef como argumento do método .aggregate() [MAIS FÁCIL]
Precificacao.aggregate([
{ $group: {
_id: {
formato: “$formato”,
precificacao: “$_id”,
nome: “$nome”,
valor: "$valor"
}
},
{ $sort: { valor: 1 } },
{ $group: {
_id: “$_id.formato”,
precificacao: { $first: “$_id.nome” },
valorMinimo: { $first: “$valor” } }
}
]).exec().then(function (precificacoes) {
// retornando o resultado da query na forma de promise
});

2. Usando o construtor de pipeline nativo do Mongoose [MAIS LEGÍVEL]

Precificacao
.aggregate()
.group({
_id: {
formato: “$formato”,
precificacao: “$_id”,
nome: “$nome”,
valor: "$valor"
}
})
.sort({ valor: 1 })
.group({
_id: “$_id.formato”,
precificacao: { $first: “$_id.nome” },
valorMinimo: { $first: “$valor” } }
})
.exec()
.then(function (precificacoes) {
// precificacoes é o resultado resolvido da promise
});

Um truque para usar esse segundo método juntamente com o MongoChef é copiar cada expressão nos stages do pipeline no MongoChef e colar em cada método do pipeline no Mongoose.

Et voilá!

--

--

Gustavo Gondim
Engenharia NOALVO

Produtos digitais e tecnologia ∴ Diretor @Eletromidia | Cofundador @NOALVO