Olá, dev! Seja bem-vindo ao Dia 08 do seu plano de estudos para a certificação MongoDB Associate Developer. Hoje aprofundaremos nas operações mais avançadas da pipeline de agregação: a manipulação de estruturas embutidas com $unwind, relacionamentos e junções entre coleções com $lookup, e agregações paralelas com $facet.
1. Desconstruindo Arrays com `$unwind`
O estágio $unwind desconstrói um campo do tipo array em um documento de entrada, gerando um documento individual para cada elemento contido no array.
// Sintaxe Simples
{ $unwind: "$itens" }
// Sintaxe Avançada com Opções
{
$unwind: {
path: "$itens",
includeArrayIndex: "posicaoItem", // Cria um campo com o índice original (0, 1, 2...)
preserveNullAndEmptyArrays: true // Mantém o documento mesmo se o array for nulo/vazio
}
}
2. Junções de Coleções com `$lookup`
O MongoDB realiza junções do tipo Left Outer Join entre duas coleções no mesmo banco de dados através do estágio $lookup.
A. Sintaxe de Correspondência de Igualdade (Equality Match)
db.pedidos.aggregate([
{
$lookup: {
from: "clientes", // Coleção de destino
localField: "clienteId", // Campo na coleção atual (pedidos)
foreignField: "_id", // Campo na coleção de destino (clientes)
as: "dadosCliente" // Campo que receberá o array de correspondências
}
}
])
B. Subconsultas e Junções Complexas (`let` e `pipeline`)
Quando a junção exige condições além da igualdade simples (ex: filtros adicionais, ordenação ou limitação no lado estrangeiro), utiliza-se a sintaxe estendida com variáveis let e sub-pipeline:
db.clientes.aggregate([
{
$lookup: {
from: "pedidos",
let: { id_cliente: "$_id" }, // Define variável local com o cifrão simples
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$clienteId", "$$id_cliente"] }, // Variável do 'let' usa cifrão duplo $$
{ $eq: ["$status", "ENTREGUE"] }
]
}
}
},
{ $sort: { dataPedido: -1 } },
{ $limit: 3 }
],
as: "ultimosPedidosEntregues"
}
}
])
3. Agregações Multi-Faceta com `$facet`
O estágio $facet permite executar múltiplas pipelines de agregação independentes e paralelas sobre o mesmo conjunto de dados de entrada em uma única etapa.
É a solução ideal para construir dashboards ou APIs de busca que precisam retornar resultados paginados e contadores por categoria em uma única requisição.
db.produtos.aggregate([
{ $match: { ativo: true } },
{
$facet: {
"resumoCategorias": [
{ $group: { _id: "$categoria", total: { $sum: 1 } } }
],
"produtosMaisBaratos": [
{ $sort: { preco: 1 } },
{ $limit: 5 },
{ $project: { nome: 1, preco: 1 } }
]
}
}
])
4. Agrupamento por Faixas com `$bucket`
Categoriza documentos em grupos baseados em limites contínuos (boundaries):
db.produtos.aggregate([
{
$bucket: {
groupBy: "$preco",
boundaries: [0, 50, 150, 500], // Faixas: [0-49.99], [50-149.99], [150-499.99]
default: "Acima de 500", // Categoria para valores fora das fronteiras
output: {
quantidade: { $sum: 1 },
produtos: { $push: "$nome" }
}
}
}
])
5. Questões no Estilo Exame de Certificação
Questão 1: Considere um documento na coleção usuarios com o campo telefones: [] (array vazio). Se executarmos a pipeline db.usuarios.aggregate([ { $unwind: "$telefones" } ]), o que acontecerá com esse documento?
A) Ele será retornado com o campo telefones definido como null.
B) Ele será descartado e não aparecerá no resultado da agregação.
C) O MongoDB lançará um erro de sintaxe BSON.
D) Ele será mantido intacto no resultado.
Resposta Correta: B. Por padrão, o $unwind descarta documentos cujo array é vazio ou nulo. Para mantê-lo, é necessário passar a opção preserveNullAndEmptyArrays: true.
Questão 2: Ao utilizar a sintaxe avançada de $lookup com o parâmetro let, como deve ser referenciada uma variável definida no let dentro do bloco $expr da sub-pipeline?
A) Utilizando aspas simples sem cifrão (ex: 'id_cliente').
B) Utilizando um único cifrão (ex: "$id_cliente").
C) Utilizando dois cifrões (ex: "$$id_cliente").
D) Utilizando a notação de ponto (ex: "vars.id_cliente").
Resposta Correta: C. Na sub-pipeline do $lookup, campos do documento estrangeiro usam "$campo" e variáveis definidas no let usam obrigatoriamente cifrão duplo "$$variavel".