MongoDB from Basics to Production · MongoDB Basics
The Aggregation Pipeline
Analyse data on the server with $match, $group, $project, $unwind, $sort and $facet stages, in the right order for performance.
What you'll build
Sales analytics on order documents: revenue per category, best-selling products, and a dashboard query that returns several summaries at once with $facet.
The idea
- An aggregation pipeline is a list of stages; each transforms the stream of documents.
- Common stages:
$match(filter),$group(aggregate with$sum,$avg,$max...),$project/$set(reshape),$unwind(one document per array element),$sort,$limit,$lookup,$facet. - Put
$match(and$sort+$limit) early, so indexes are used and later stages process fewer documents.
The code
db.orders.insertMany([
{ _id: 1, status: "paid", lines: [{ sku: "mug", category: "kitchen", qty: 2, price: 8.5 }, { sku: "tea", category: "food", qty: 3, price: 12 }] },
{ _id: 2, status: "paid", lines: [{ sku: "teapot", category: "kitchen", qty: 1, price: 24 }] },
{ _id: 3, status: "refunded", lines: [{ sku: "kettle", category: "appliances", qty: 1, price: 31.9 }] },
{ _id: 4, status: "paid", lines: [{ sku: "mug", category: "kitchen", qty: 4, price: 8.5 }] },
]);
print("revenue by category:");
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $unwind: "$lines" },
{ $group: { _id: "$lines.category", revenue: { $sum: { $multiply: ["$lines.qty", "$lines.price"] } }, units: { $sum: "$lines.qty" } } },
{ $sort: { revenue: -1 } },
]).forEach(r => print(" ", r._id, r.revenue, "units:", r.units));
const [dash] = db.orders.aggregate([
{ $facet: {
byStatus: [{ $group: { _id: "$status", n: { $sum: 1 } } }, { $sort: { _id: 1 } }],
topSku: [{ $match: { status: "paid" } }, { $unwind: "$lines" },
{ $group: { _id: "$lines.sku", units: { $sum: "$lines.qty" } } }, { $sort: { units: -1 } }, { $limit: 1 }],
} },
]).toArray();
print("orders by status:", JSON.stringify(dash.byStatus));
print("top product:", JSON.stringify(dash.topSku));
Output
revenue by category:
kitchen 75 units: 7
food 36 units: 3
orders by status: [{"_id":"paid","n":3},{"_id":"refunded","n":1}]
top product: [{"_id":"mug","units":6}]Run this on your computer — see “Get Started” for setup.
Common mistakes
$matchafter$unwindor$groupwhen it could filter first, processing far more documents than needed.- Running heavy analytics pipelines on the primary during peak traffic; use secondaries or analytics nodes for reporting.
- Large
$groupstages exceeding memory limits; filter first, or allow disk use for batch jobs.
Key points
- Pipelines transform documents stage by stage.
- Filter early with $match so indexes and smaller inputs help.
- $facet computes several summaries in one query.
Further reading: Aggregation pipeline · Aggregation pipeline optimization
Practice questions
Why put $match as early as possible in a pipeline?
Answer
Early filtering can use indexes and reduces the documents every later stage has to process.
What does $unwind do?
Answer
It outputs one document per element of an array field, so array elements can be grouped or filtered individually.
What is $facet useful for?
Answer
Running several sub-pipelines over the same input in one query, such as counts and top-N lists for a dashboard.
Open in the app to mark lessons complete, take notes and track your progress.