NoSQL · Data models beyond tables
How does the MongoDB aggregation pipeline work?
Short answer
An aggregation pipeline passes documents through stages such as match, group, project, unwind and lookup. Put selective filters early, and watch memory limits and lookup cost.
Each stage transforms the stream and passes it on. The optimizer can reorder some stages, but you should still filter early and project only the fields you need. Grouping and sorting are the expensive stages because they may need to hold data in memory.
db.orders.aggregate([
{ $match: { status: "paid", createdAt: { $gte: new Date("2026-09-01") } } },
{ $unwind: "$items" },
{ $group: {
_id: "$items.sku",
revenue: { $sum: { $multiply: ["$items.price", "$items.qty"] } },
orders: { $sum: 1 }
} },
{ $sort: { revenue: -1 } },
{ $limit: 10 }
]);Stages worth knowing
- $match filters documents and should come as early as possible
- $project shapes the output and can reduce memory use
- $group aggregates by a key and accumulates values
- $unwind expands an array into one document per element
- $lookup performs a left outer join and is the most expensive common stage
- $facet runs several pipelines over the same input for dashboards
A $lookup across a large collection without an index on the foreign field can be very slow. If the joined data is needed on every read, consider denormalizing it instead. Use allowDiskUse for large aggregations, but treat it as a signal to optimize rather than a permanent fix.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1Why should $match come before $unwind?
- 2How do you make a $lookup faster?
- 3What is the aggregation memory limit?
- 4When would you use $facet?
Say it in the interview
Aggregation is a pipeline where each stage transforms the document stream. I put selective match stages first, project only the fields I need, and keep grouping and sorting late. Lookups are the expensive part, especially without an index on the foreign field, so I consider denormalizing instead. I also watch the per-stage memory limit and use allowDiskUse only as a temporary escape hatch, not as the design.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live NoSQL mock interview and get scored feedback on depth, clarity and confidence.