MongoDB Aggregation

Beginner
14 min read
Updated: Oct 2026
Concept guide
Pipeline stages

What Is MongoDB Aggregation?

MongoDB Aggregation is a framework for processing documents through a sequence of stages to filter, transform, group, calculate, and analyze data.

Use it when a normal query is not enough to produce the result you need.

Remember
Documents
    ↓
Aggregation Pipeline
    ↓
Filter
    ↓
Transform
    ↓
Group
    ↓
Sort
    ↓
Results

Aggregation = overall data-processing framework
Pipeline    = ordered sequence of stages
Stage       = one processing step

Why Use Aggregation?

Aggregation lets you combine several processing steps in one pipeline: filter, transform, calculate, group, sort, work with arrays, combine related collections, reshape data, and build reports or analytics.

mongosh
db.sales.aggregate([
  {
    $match: {
      status: "completed"
    }
  }
])

How It Works

  1. aggregate() starts a pipeline on the sales collection.
  2. $match keeps only documents with status: "completed".
  3. Those filtered documents become the pipeline result.

Aggregation Pipeline

An aggregation pipeline is an ordered sequence of stages. Each stage receives documents, processes them, and passes its output to the next stage.

mongosh
db.sales.aggregate([
  {
    $match: {
      status: "completed"
    }
  },
  {
    $group: {
      _id: "$category",
      totalSales: {
        $sum: "$amount"
      }
    }
  },
  {
    $sort: {
      totalSales: -1
    }
  }
])
Document flow
Documents
    ↓
  $match
    ↓
  $group
    ↓
  $sort
    ↓
 Results

How It Works

  1. $match filters the documents.
  2. $group groups the remaining documents.
  3. $sum calculates a total inside $group (it is an accumulator in this example, not a pipeline stage).
  4. $sort sorts the grouped results.
  5. MongoDB returns the final documents.

Pipeline Stages

Each stage performs one specific operation. This table introduces common stages—not every stage MongoDB supports.

StagePurpose
$matchFilters documents
$projectSelects or reshapes fields
$setAdds or modifies fields
$addFieldsAdds or modifies fields
$groupGroups documents
$sortSorts documents
$limitLimits results
$skipSkips documents
$unwindExpands array elements
$lookupCombines related collections
$countCounts documents
$facetRuns multiple sub-pipelines

Aggregation Stage vs Expression

Keep these concepts separate: a stage is a pipeline step; an expression calculates or produces a value inside a stage.

Stage

Stage
{
  $match: {
    status: "completed"
  }
}

Expression

Expression inside $project
{
  $project: {
    total: {
      $add: ["$price", "$tax"]
    }
  }
}
Picture
Pipeline
   |
   +-- Stage
   |     |
   |     +-- may contain expressions
   |
   +-- Stage
         |
         +-- may contain expressions

$match is the stage. $add is an expression used inside $project. Browse more expressions in MongoDB Operators.

Where Do Accumulators Fit?

Accumulators calculate values from multiple input values or documents in aggregation contexts. They are not the same topic as Aggregation itself.

Inside $group
{
  $group: {
    _id: "$category",
    total: {
      $sum: "$amount"
    },
    average: {
      $avg: "$amount"
    }
  }
}
Concept map
Aggregation
     ↓
Pipeline
     ↓
$group Stage
     ↓
Accumulators
 ┌───────────────┐
 │ $sum          │
 │ $avg          │
 └───────────────┘
  • $group is an aggregation stage.
  • $sum and $avg are accumulators in this example.
  • The pipeline contains the stage; the accumulators calculate values within that stage.

Learn more about MongoDB Accumulators →

Pseudocode

Flow
Read documents
      ↓
Run Stage 1
      ↓
Pass results to Stage 2
      ↓
Run Stage 2
      ↓
Pass results to Stage 3
      ↓
Return final results
Loop form
FOR each pipeline stage
    process the current documents
    pass the result to the next stage
RETURN final documents

$match

$match filters documents so only matching ones continue through the pipeline.

mongosh
db.orders.aggregate([
  {
    $match: {
      status: "completed"
    }
  }
])

How It Works

  1. MongoDB examines each input document.
  2. Documents with status: "completed" pass through.
  3. Other documents leave the pipeline stream.

$project

$project selects or reshapes fields.

mongosh
db.products.aggregate([
  {
    $project: {
      name: 1,
      price: 1
    }
  }
])

How It Works

  1. MongoDB processes each document.
  2. Only the requested fields are included in the output (plus _id unless excluded).
  3. The pipeline continues with the reshaped documents.

$set

$set adds or modifies fields. Expressions inside it calculate new values.

mongosh
db.products.aggregate([
  {
    $set: {
      discountedPrice: {
        $multiply: ["$price", 0.9]
      }
    }
  }
])

How It Works

  1. $set is the stage that adds or updates a field.
  2. $multiply is the expression that computes price × 0.9.
  3. The new discountedPrice field is passed to later stages.

$group

$group combines documents that share a grouping key. Accumulators can calculate values for each group.

mongosh
db.sales.aggregate([
  {
    $group: {
      _id: "$category",
      totalSales: {
        $sum: "$amount"
      }
    }
  }
])

How It Works

  1. Documents are grouped by category (_id is the grouping key).
  2. Each category becomes one group.
  3. $sum adds amount values inside that group (accumulator, not a stage).
  4. One output document is produced per group.

For accumulator operators and patterns, see MongoDB Accumulators.

$sort

$sort orders documents. Use 1 for ascending and -1 for descending.

mongosh
db.products.aggregate([
  {
    $sort: {
      price: -1
    }
  }
])

How It Works

  1. Documents reaching $sort are ordered by price.
  2. -1 puts higher prices first.
  3. Sorted documents continue to the next stage.

$limit and $skip

$limit keeps only the first N documents that reach it. $skip discards the first N documents—useful for simple pagination patterns.

mongosh
db.products.aggregate([
  {
    $sort: {
      price: -1
    }
  },
  {
    $limit: 5
  }
])

How It Works

  1. $sort orders products by price (highest first).
  2. $limit: 5 keeps only the first five sorted documents.
mongosh
db.products.aggregate([
  {
    $skip: 10
  },
  {
    $limit: 10
  }
])

How It Works

  1. $skip: 10 drops the first 10 documents.
  2. $limit: 10 keeps the next 10—a basic page-style window.
  3. For very large collections, other pagination strategies may scale better than large skips.

$unwind

$unwind expands an array field so each element becomes its own pipeline document.

Input document
{
  name: "Laptop",
  tags: ["computer", "electronics"]
}
Stage
{
  $unwind: "$tags"
}
Picture
One document
     ↓
  $unwind
     ↓
Multiple documents

{ name: "Laptop", tags: "computer" }
{ name: "Laptop", tags: "electronics" }

How It Works

  1. MongoDB reads the tags array.
  2. Each array element becomes a separate document in the stream.
  3. Other fields (like name) are copied onto each output document.

$lookup

$lookup performs a left outer join with another collection in the same database. Matching documents are added as an array field.

Picture
orders
   |
   v
$lookup
   |
   v
customers
   |
   v
combined result
(joined docs in an array field)
mongosh
db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customerId",
      foreignField: "_id",
      as: "customer"
    }
  }
])

How It Works

  1. For each order, MongoDB looks up matching customers.
  2. Matches are placed in the customer array field.
  3. Orders without matches still continue, with an empty array.

Worked Walkthrough — Sales by Category

Group sales by category, total the amounts, then sort highest first.

mongosh
db.sales.insertMany([
  { product: "Laptop", category: "Electronics", amount: 1000 },
  { product: "Mouse",  category: "Electronics", amount: 50 },
  { product: "Chair",  category: "Furniture",   amount: 200 },
  { product: "Desk",   category: "Furniture",   amount: 400 }
])
mongosh
db.sales.aggregate([
  {
    $group: {
      _id: "$category",
      totalSales: {
        $sum: "$amount"
      }
    }
  },
  {
    $sort: {
      totalSales: -1
    }
  }
])

How It Works

  1. MongoDB reads the sales documents.
  2. $group creates a group for each category.
  3. $sum adds amount values inside each group (accumulator in the aggregation pipeline).
  4. $sort orders groups by totalSales descending.
  5. MongoDB returns the final grouped documents.

Multiple Stages Together

A typical report pipeline filters first, then groups, sorts, and limits.

mongosh
db.sales.aggregate([
  {
    $match: {
      status: "completed"
    }
  },
  {
    $group: {
      _id: "$category",
      totalSales: {
        $sum: "$amount"
      }
    }
  },
  {
    $sort: {
      totalSales: -1
    }
  },
  {
    $limit: 5
  }
])
Flow
All Sales
   ↓
$match
   ↓
Completed Sales
   ↓
$group
   ↓
Category Totals
   ↓
$sort
   ↓
Highest First
   ↓
$limit
   ↓
Top 5

How It Works

  1. $match keeps completed sales only.
  2. $group totals amount per category.
  3. $sort puts the largest totals first.
  4. $limit returns only the top 5 groups.

Aggregation vs find()

Aggregation does not replace find(). Use the simplest operation that solves the problem.

find()aggregate()
Retrieves matching documentsProcesses documents through stages
Best for straightforward queriesUseful for complex transforms and analysis
Primarily filters documentsCan filter, transform, group, calculate, sort, join, reshape
Returns matching documentsReturns the result of the pipeline

Pipeline Order & Filtering Early

Stage order matters. Changing order can change the result, how many documents pass between stages, and how much work the pipeline does.

Common pattern
db.sales.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$category", totalSales: { $sum: "$amount" } } },
  { $sort: { totalSales: -1 } }
])

$match → $group → $sort

Filtering early often reduces later work—but $match is not required to be first in every pipeline, and MongoDB's optimizer can reorder some operations when it is safe to do so.

Fewer docs later
Many Documents
      ↓
   $match
      ↓
Fewer Documents
      ↓
$group / $sort / ...

Arrays, joins, and output

  • Arrays: inspect array fields with expressions, or use $unwind to turn elements into separate documents.
  • Multiple collections: $lookup combines related data; joined matches usually arrive as an array field.
  • Output: most pipelines return a result cursor. $out and $merge can write results to a collection.

Edge Cases & Pitfalls

Common mistakes when learning MongoDB aggregation.

order

Stage order can affect results

Sorting before grouping is not the same as grouping before sorting. Design the flow intentionally.

shape

$group changes document shape

Output focuses on _id plus accumulator fields. Original fields are not kept unless you include them in the group expression.

key

_id is the grouping key

Whatever you put in _id defines the buckets. Wrong key → wrong groups.

project

$project can drop fields

Later stages only see fields you kept or computed. Project carefully before grouping or sorting.

unwind

$unwind multiplies documents

An array of length 5 can turn 1 document into 5. Plan for larger intermediate streams.

lookup

$lookup returns an array

Joined matches land in the as field as an array—even when there is zero or one match.

null

Missing and null values matter

Expressions and grouping treat missing or null fields differently from numbers or strings. Check your data types.

terms

Do not confuse the three layers

Aggregation ≠ accumulators. Stages ≠ expressions. Accumulators are not pipeline stages.

speed

Aggregation is not automatically faster

A pipeline can do more work than find(). Prefer the simplest tool that meets the need.

Performance Notes

IdeaPractical tip
Filter earlyReduce unnecessary documents before heavy stages when it fits the result you need
IndexesSuitable $match / $sort patterns can use indexes
$group / $sortLarge groups and sorts can be memory- and CPU-heavy
$lookupCost depends on data size, keys, and how many matches are found
$unwindCan multiply document count in the pipeline
ExplainUse explain / query analysis for important pipelines
ContextResults depend on data size, indexes, pipeline shape, workload, and MongoDB version

Avoid absolute claims. Filtering can reduce work, but it does not guarantee a fast pipeline by itself.

Cheat Sheet

ConceptRemember
AggregationProcesses documents through a pipeline
PipelineOrdered sequence of stages
StagePerforms one processing step
ExpressionProduces or calculates a value
$matchFilters documents
$projectShapes documents
$setAdds or modifies fields
$groupGroups documents
$sortSorts documents
$limitLimits results
$skipSkips documents
$unwindExpands arrays
$lookupCombines related collections
AccumulatorCalculates values in supported aggregation contexts — see Accumulators

Key Takeaways

  • MongoDB Aggregation processes documents through a pipeline.
  • A pipeline contains ordered stages; each stage does one job.
  • $match filters; $project / $set reshape; $group combines.
  • Accumulators such as $sum calculate values in aggregation contexts—they are not the Aggregation topic itself.
  • $unwind works with arrays; $lookup combines related collections.
  • Pipeline design affects both results and performance.
  • Aggregation and accumulators are related but not the same concept.

One line: Aggregation runs documents through ordered stages; stages may use expressions; $group may use accumulators—keep those layers distinct.

Frequently Asked Questions

Aggregation is a framework for processing documents through a sequence of stages to filter, transform, group, calculate, and analyze data.
A pipeline is an ordered array of stages passed to db.collection.aggregate([ ... ]). Each stage receives documents, processes them, and passes results to the next stage.
A stage is one processing step in the pipeline—for example $match to filter or $group to combine documents. Stages run in the order you list them.
A stage controls a pipeline step. An expression calculates or produces a value inside a stage—for example $add inside $project, or $multiply inside $set.
Aggregation is the overall document-processing framework (pipeline + stages). Accumulators are calculation operators used in supported aggregation contexts—most commonly inside $group—to compute values from multiple inputs.
An accumulator calculates a value from multiple input values or documents in aggregation contexts. Examples include $sum and $avg inside $group. See the Accumulators section for the full topic.
$match filters documents so only matching ones continue to the next stage. Its query syntax is similar to find() filters.
$group combines documents that share the same grouping key (_id) and can calculate values for each group using accumulators such as $sum.
$project selects, excludes, or reshapes fields so later stages see a shaped document.
$unwind expands an array field so each array element becomes its own pipeline document.
$lookup performs a left outer join with another collection in the same database and adds matching documents as an array field.
find() retrieves matching documents. aggregate() processes documents through stages and can filter, transform, group, calculate, sort, join, and reshape in one pipeline.
Yes, for suitable patterns—especially early $match (and some $sort) stages that align with indexes. Index use depends on the pipeline, data, and planner.
No for a normal read pipeline. Results are returned from the pipeline. Stages like $out or $merge can write results to a collection.
Yes via $lookup, which performs a left outer join with another collection and returns matches in an array field—not a full SQL-style join engine.
Yes. Order can change results, how many documents flow between stages, and how much work the pipeline does. The optimizer may reorder some operations when safe.

Next: Replication

Keep multiple live copies of your data with replica sets.

Replication tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

14 people found this page helpful