intermediate
Aggregation pipeline
Use pipeline stages to filter, reshape, group, join, and compute data without moving all work into application code.
The aggregation pipeline processes documents through ordered stages — like a server-side dataflow. Use it to filter, reshape, group, join, and compute analytics close to data instead of shipping large result sets to the app.
db.orders.aggregate([
{ $match: { status: "paid", createdAt: { $gte: ISODate("2026-06-01") } } },
{ $unwind: "$items" },
{ $group: {
_id: "$items.sku",
revenue: { $sum: { $multiply: ["$items.qty", "$items.price"] } },
orders: { $addToSet: "$_id" }
}},
{ $project: { sku: "$_id", revenue: 1, orderCount: { $size: "$orders" } } },
{ $sort: { revenue: -1 } },
{ $limit: 10 }
])
| Stage | Role | |-------|------| | `$match` | Filter early; should use indexes | | `$group` / `$bucket` | Aggregations and histograms | | `$lookup` | Left-outer join to another collection | | `$facet` | Multiple sub-pipelines in one round trip | | `$set` / `$project` | Shape output fields |
`$match` and `$sort` early reduce working set size. `$lookup` is powerful but expensive at scale — prefer embedding or targeted pre-aggregation when possible. Memory limits apply unless `allowDiskUse` is enabled.
On interviews: sketch stages for a reporting question and say where indexes must support `$match`.
Common pitfalls: `$lookup` on unindexed foreign fields; `$unwind` exploding cardinality before `$group`; running aggregation without `explain`; moving trivial filters to the application after fetching too many docs.
The trade-off is server-side compute and pipeline complexity versus network and application memory — the pipeline wins when it eliminates large transfers and duplicate business logic.
Checklist:
- Put selective `$match` first.
- Name stages for filter, join, group, and project.
- Check index support on matched fields.
- Estimate cardinality after `$unwind` and `$lookup`.