$match
Filter documents (like find)
{ $match: { city: "Chennai" } } 
MongoDB aggregation stages are the steps in a pipeline. Each stage starts with $ and does one job: filter, reshape, group, sort, join, or write results.
You pass an ordered array of stages to db.collection.aggregate([ ... ]). Documents flow through each stage in order—like an assembly line for analytics and reports.
Stage = one pipeline step that starts with $
Examples:
$match → filter documents
$project → reshape fields
$group → summarize with accumulators
$lookup → join another collection
Browse every tutorial in the left sidebar. A pipeline is an array. MongoDB runs stage 1 on the collection, feeds its output into stage 2, and so on until the final result cursor.
aggregate([ ... ]).$out / $merge).| Where stages appear | Typical use |
|---|---|
| Filter early | $match to shrink the stream |
| Reshape fields | $project / $addFields |
| Summarize | $group with accumulators |
| Join collections | $lookup + optional $unwind |
| Order & page | $sort, $skip, $limit |
// Filter → group → sort
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$storeId", revenue: { $sum: "$amount" } } },
{ $sort: { revenue: -1 } }
])
// Add a computed field without dropping others
db.orders.aggregate([
{ $addFields: { total: { $multiply: ["$qty", "$price"] } } }
]) $match. Keeps only completed orders.$group. Totals amount per storeId.$sort. Highest revenue first.$addFields. Adds total while keeping existing fields.Almost every stage uses the { $stageName: <spec> } form inside an aggregation array. The $ prefix marks a reserved stage name.
db.collection.aggregate([
{ $stageName: <specification> },
{ $anotherStage: <specification> }
])
// Field path inside expressions:
"$fieldName" → value of that field on the current document $matchFilter documents (like find)
{ $match: { city: "Chennai" } } $projectInclude, exclude, or compute fields
{ $project: { name: 1, total: { $add: ["$a", "$b"] } } } $groupBucket and roll up with accumulators
{ $group: { _id: "$cat", n: { $sum: 1 } } } $lookupLeft-outer join another collection
{ $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } } Use this mental model before you build a pipeline.
goal = report / join / reshape / write results
stream = all documents in collection
for each stage in pipeline (in order):
if stage is $match:
stream = keep only matching docs
else if stage is $project / $addFields:
stream = reshape each document
else if stage is $group:
stream = one doc per bucket + accumulators
else if stage is $sort / $limit:
stream = ordered / trimmed stream
else:
stream = apply that stage's rule
return stream Major families below. The left sidebar lists every tutorial—this section only explains when each family is useful.
| Category | Used for | Examples |
|---|---|---|
| Filter | Keep documents that match a condition | $match |
| Transform | Reshape, add, remove, or replace fields | $project, $addFields, $unset |
| Group & analyze | Summaries, histograms, multi-view results | $group, $bucket, $facet |
| Sort & page | Order results and take a window of rows | $sort, $skip, $limit |
| Join & arrays | Relate collections or flatten arrays | $lookup, $unwind, $graphLookup |
| Geo & search | Nearness queries and Atlas Search | $geoNear, $search |
| Write | Persist pipeline output to a collection | $out, $merge |
| Diagnostic | Stats, sessions, ops, and change streams | $collStats, $indexStats, $changeStream |
For rollups inside $group, see the accumulators hub. For per-document expressions, see the operators hub.
A short starter set. Open any name for a full tutorial; use the sidebar for the complete catalog.
| Stage | Quick meaning |
|---|---|
$match | Filter documents (find-style query) |
$project | Choose or compute output fields |
$addFields / $set | Add fields while keeping the rest |
$group | Bucket documents and run accumulators |
$sort | Order the stream by one or more fields |
$limit / $skip | Take / offset documents (pagination) |
$lookup | Join another collection |
$unwind | Flatten an array into one doc per element |
$count | Return { count: N } for the stream |
$facet | Run several sub-pipelines in one query |
Small orders collection. We filter completed orders, group by store, then sort by revenue.
Four orders across two stores.
db.orders.insertMany([
{ _id: 1, storeId: "A", amount: 40, status: "completed" },
{ _id: 2, storeId: "A", amount: 60, status: "completed" },
{ _id: 3, storeId: "B", amount: 25, status: "completed" },
{ _id: 4, storeId: "A", amount: 15, status: "cancelled" }
]); orders.A has two completed orders and one cancelled.B has one completed order.Three stages: keep completed rows, total revenue per store, then rank stores.
db.orders.aggregate([
{ $match: { status: "completed" } },
{
$group: {
_id: "$storeId",
revenue: { $sum: "$amount" },
orders: { $sum: 1 }
}
},
{ $sort: { revenue: -1 } }
]); Match the job to the family first; then pick a specific stage.
| If you need to… | Start with |
|---|---|
| Filter rows | $match |
| Keep most fields and add a few | $addFields / $set |
| Control exactly which fields survive | $project |
| Totals or averages by category | $group + accumulators |
| Join another collection | $lookup |
| Flatten an array field | $unwind |
| Several views in one query | $facet |
| Save results to a collection | $out or $merge |
Common beginner traps when learning MongoDB stages.
Put $match early to use indexes and shrink work. Sort before $group when $first / $last must be stable.
$project can drop fields you still needInclusive projections keep only listed fields (plus _id unless excluded). Prefer $addFields when you only want to add columns.
$geoNear must be firstWhen you use $geoNear, it has to be the first stage in the pipeline.
$out / $merge change dataMost stages only return a result cursor. Write stages persist output—place them last and test carefully.
$unwind multiplies documentsAn array of length N becomes N documents. Empty or missing arrays can drop rows unless you set preserveNullAndEmptyArrays.
Stages are pipeline steps. Expression operators run inside stages. Accumulators roll up values inside $group.
| Idea | Practical tip |
|---|---|
| Stages are not “slow by default” | Cost comes from how much data each stage must process |
| Filter early | Place selective $match near the start |
| Indexes matter | Early $match / $sort can use indexes when the shape allows |
| Project less | Drop unused fields before heavy $group or $lookup |
| Limit while testing | Add temporary $limit while building pipelines |
Use explain() when a pipeline feels slow. Prefer selective filters and smaller working sets over rearranging stages without a clear reason.
One line: Aggregation stages are the steps of the pipeline—order them to filter, transform, summarize, and deliver results.
Add computed fields while keeping the rest of each document.
11 people found this page helpful