Volver a MongoDB Básico
Pipeline de Agregación en MongoDB
Concepto del Pipeline
Cada etapa transforma documentos y pasa los resultados a la siguiente:
Colección → [$match] → [$group] → [$sort] → [$project] → Resultado
Etapas Principales
// $match — filtrar (usa índices, ponerlo primero)
{ $match: { estado: "activo", edad: { $gte: 18 } } }
// $project — remodelar documentos
{
$project: {
nombreCompleto: { $concat: ["$nombre", " ", "$apellido"] },
anio: { $year: "$creado_en" },
_id: 0
}
}
// $group — agregar
{
$group: {
_id: "$categoria",
total: { $sum: "$precio" },
promedio: { $avg: "$precio" },
conteo: { $sum: 1 },
maxPrecio: { $max: "$precio" },
productos: { $push: "$nombre" }
}
}
// $sort, $limit, $skip
{ $sort: { total: -1 } }
{ $limit: 10 }
{ $skip: 20 }
$lookup (JOIN)
db.pedidos.aggregate([
{
$lookup: {
from: "clientes",
localField: "clienteId",
foreignField: "_id",
as: "cliente"
}
},
{ $unwind: "$cliente" },
{
$project: {
pedidoId: "$_id",
total: 1,
nombreCliente: "$cliente.nombre"
}
}
])
$unwind
// Deconstruir campo array
db.publicaciones.aggregate([
{ $unwind: "$etiquetas" },
{ $group: { _id: "$etiquetas", conteo: { $sum: 1 } } },
{ $sort: { conteo: -1 } }
])
$addFields y $bucket
// Agregar campos calculados
{
$addFields: {
totalConIva: { $multiply: ["$total", 1.16] },
anio: { $year: "$fechaPedido" }
}
}
// Agrupar en rangos
{
$bucket: {
groupBy: "$precio",
boundaries: [0, 50, 100, 200, 500],
default: "500+",
output: { conteo: { $sum: 1 }, promedio: { $avg: "$precio" } }
}
}
Ejemplo Real: Ingresos Mensuales
db.pedidos.aggregate([
{ $match: { estado: "completado", creado_en: { $gte: new Date("2024-01-01") } } },
{
$addFields: {
mes: { $month: "$creado_en" },
anio: { $year: "$creado_en" }
}
},
{
$group: {
_id: { anio: "$anio", mes: "$mes" },
ingresos: { $sum: "$total" },
numPedidos: { $sum: 1 },
promedioP: { $avg: "$total" }
}
},
{ $sort: { "_id.anio": 1, "_id.mes": 1 } },
{
$project: {
_id: 0,
periodo: { $concat: [
{ $toString: "$_id.anio" }, "-",
{ $toString: "$_id.mes" }
]},
ingresos: { $round: ["$ingresos", 2] },
numPedidos: 1,
promedioP: { $round: ["$promedioP", 2] }
}
}
])
$facet (Múltiples Pipelines)
db.productos.aggregate([
{ $match: { categoria: "Electrónica" } },
{
$facet: {
rangosPrecios: [
{ $bucket: { groupBy: "$precio", boundaries: [0, 100, 500, 1000] } }
],
topMarcas: [
{ $group: { _id: "$marca", conteo: { $sum: 1 } } },
{ $sort: { conteo: -1 } },
{ $limit: 5 }
],
totalResultados: [
{ $count: "total" }
]
}
}
])