The MongoDB Aggregation Pipeline by Example
The aggregation pipeline processes documents through a series of stages, each transforming the output of the previous one. It's MongoDB's answer to SQL's GROUP BY, joins and reporting queries.
Revenue per day
const revenue = await orders.aggregate([
{ $match: { status: 'paid', createdAt: { $gte: new Date('2026-10-01') } } },
{ $unwind: '$items' }, // one document per item
{
$group: {
_id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },
revenue: { $sum: { $multiply: ['$items.qty', '$items.price'] } },
orders: { $addToSet: '$_id' },
},
},
{ $project: { _id: 0, day: '$_id', revenue: 1, orderCount: { $size: '$orders' } } },
{ $sort: { day: 1 } },
]).toArray();
Common stages
$match- filter (like WHERE). Put it first so indexes can be used.$group- aggregate with$sum,$avg,$min,$max,$push.$project/$addFields- reshape documents and compute fields.$unwind- turn an array into one document per element.$sort,$limit,$skip.$lookup- join with another collection.
Joining with $lookup
const ordersWithCustomer = await orders.aggregate([
{ $match: { status: 'paid' } },
{ $limit: 20 },
{
$lookup: {
from: 'customers',
localField: 'customerId',
foreignField: '_id',
as: 'customer',
pipeline: [{ $project: { name: 1, email: 1 } }],
},
},
{ $unwind: '$customer' },
]).toArray();
Several reports in one query with $facet
const [stats] = await products.aggregate([
{ $match: { active: true } },
{
$facet: {
byCategory: [{ $group: { _id: '$category', count: { $sum: 1 } } }],
priceStats: [{ $group: { _id: null, avg: { $avg: '$price' }, max: { $max: '$price' } } }],
newest: [{ $sort: { createdAt: -1 } }, { $limit: 5 }, { $project: { name: 1 } }],
},
},
]).toArray();
Stages each have memory limits; large sorts and groups may need allowDiskUse. Filter early, project away unneeded fields, and run heavy analytics on a secondary or analytics node.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.