Stop 07 · NoSQL DatabasesTopics · 5/12

Aggregation Pipeline

Part of the Backend Developer Roadmap

In short

Need 'top five dishes by revenue this week'? In MongoDB that's an aggregation pipeline: documents flow through stages, and each stage filters, groups or reshapes them.

MongoDB's framework for transforming and analyzing data through a sequence of stages (`$match`, `$group`, `$sort`) — the NoSQL equivalent of complex SQL queries.

The blob works a conveyor belt where mangoes are filtered and grouped into crates.

Think of it like this

The mango sorting line

At the mandi, mangoes roll down a belt. The first station throws out unripe ones. The next groups them by size. The last counts crates per size and lines them up, biggest first. Each station does one job and passes the rest along.

Watch it happen

Top five dishes this week

$match
$unwind
$group
$sort
$limit

1/5$match keeps only this week's paid orders. Everything else is dropped right away.

The big ideas

What you need to know

Stages run in order

Each stage takes documents in and passes documents out. The order of stages matters a lot.

Match early

Put $match first so later stages handle fewer documents and an index can help.

$group does the maths

Group by a field and compute $sum, $avg, $min or $max for each group.

$lookup joins

Pulls matching documents from another collection, a bit like a LEFT JOIN. Use it sparingly.

See it in code

Walk through the code

js
1db.orders.aggregate([2  { $match: { status: "paid", placedAt: { $gte: ISODate("2026-10-20") } } },3  { $unwind: "$items" },4  { $group: {5      _id: "$items.dish",6      revenue: { $sum: { $multiply: ["$items.price", "$items.qty"] } },7      orders: { $sum: 1 },8  } },9  { $sort: { revenue: -1 } },10  { $limit: 5 },11]);

1/4Keep only paid orders from this week. An index on status and placedAt makes this fast.

A $ in front of a field name, like '$items.qty', means 'the value of this field'. Run this in mongosh.

Save yourself the pain

Common mistakes

Oops

Putting $match after $group.

Do this instead

Filter first. Matching early shrinks the data and lets MongoDB use indexes.

Oops

Using $lookup in every pipeline to rebuild SQL joins.

Do this instead

If you join on every read, rethink the document shape. Embed what you read together.

Your mission

Canteen report

~30 min

Quick check

Why should $match usually be the first stage?

Go deeper

  1. 1Build a pipeline that groups documents and computes a sum or average.
  2. 2Chain `$match` and `$group` stages to filter before aggregating.
  3. 3Compare an aggregation pipeline to the equivalent SQL `GROUP BY` query.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge