MongoDB aggregation pipelines


What is aggregation pipelines in mongoDB ?
The Aggregation pipeline is a very powerful tool in mongoDB it is used to process data through various operators and return computed results. MongoDB aggregates data through multiple steps and at the end it return the computed result.
In this a aggregation pipeline visual is shown.
1- Firstly we find document using $match operator
2- Then we group them using $group operator
3- $sort in this we sort out data either it should be in ascending order of price or descending order of price.
4- At the end it gives the computed result of whole pipeline.
this is code of this aggregation pipeline
db.orders.aggregate([
{ $match: { status: "delivered" } },
{
$group: {
_id: "$customerId",
totalSpent: { $sum: "$amount" },
orders: { $push: "$_id" }
}
},
{ $sort: { totalSpent: -1 } },
])
There are 13 operators which are commonly used
1- $match
- Filtering documents (like find
)
This operator is used to filter out document based on given conditions
✅ Example: - we want to filter out document based on that user’s age is greater or equal to 18 or not so we can use it like this
db.users.aggregate([
{ $match: { age: { $gte: 18 } } }
])
2- $project
(Select or modify fields)
This operator is used to include ,exclude ,rename ,remove any field from document.
✅ Example: - we want to send specific fields like name and age only to the user then we can also use it to add new field like isAdult like this
db.users.aggregate([
{
$project: {
name: 1,
age: 1,
isAdult: { $gte: ["$age", 18] }
}
}
])
3- $group
(Group documents and perform aggregations)
we can use this operator to group documents and then we perform aggregation operation in this like $sum , $sort, $max etc.
✅ Example: if we want to group orders by (customerId) and find total orders and total amount of order per customer so we do something like this
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" },
totalOrders: { $sum: 1 }
}
}
])
4. $sort
– Sorting the documents
we use this operator to sort the document based on specific field like pricing ,age etc.
✅ Example: form previous example if you want prices of items in increasing order then you use $sort : {item : 1}
db.products.aggregate([
{ $sort: { price: -1 } } // descending order
])
5. $limit
– Limit the number of documents
we use this operator when we need to send some limited amount of documents . Let’s say you want to send 5 document which are expensive then you use this .
db.products.aggregate([
{ $limit: 5 }
])
6. $skip
– Skip n
number of documents
This $skip
operator is used to skip some no. of documents.
✅ Example: You want to skip 5 document from starting you can do like this
db.products.aggregate([
{ $skip: 5 }
])
7. $unwind
– Deconstruct arrays into individual documents
This $unwind
operator is used when you want specific documents for array .
✅ Example: you have a document on which there is an array of hobbies and you want specific document for those hobbies in this case you use this to unwind a field of array.
db.users.aggregate([
{ $unwind: "$hobbies" }
])
For example - if you have documents like this
[
{
"_id": 1,
"name": "Ravi",
"hobbies": ["cricket", "music", "traveling"]
},
{
"_id": 2,
"name": "Anita",
"hobbies": ["reading", "painting"]
}
]
then after using unwind operator you get output like this
[
{ "_id": 1, "name": "Ravi", "hobbies": "cricket" },
{ "_id": 1, "name": "Ravi", "hobbies": "music" },
{ "_id": 1, "name": "Ravi", "hobbies": "traveling" },
{ "_id": 2, "name": "Anita", "hobbies": "reading" },
{ "_id": 2, "name": "Anita", "hobbies": "painting" }
]
8. $lookup
– Join with another collection
$lookup
is an aggregation stage used to join data from two collections, similar to a SQL JOIN
.
It allows you to combine documents from different collections based on a common field.
It accepts four fields which are mandatory to provide -
from
- which collection you want to join it accepts its namelocalField
- the field in the current (input) collectionforeignField
- the field in the foreign collectionas
- the name of the new array field to store matched docs
📂 Sample Data:
orders
collection:
jsonCopyEdit[
{ "_id": 1, "orderNumber": "ORD001", "customerId": 101, "amount": 300 },
{ "_id": 2, "orderNumber": "ORD002", "customerId": 102, "amount": 500 }
]
customers
collection:
jsonCopyEdit[
{ "_id": 101, "name": "Alice", "email": "alice@example.com" },
{ "_id": 102, "name": "Bob", "email": "bob@example.com" }
]
🧪 Aggregation Query using $lookup
:
jsCopyEditdb.orders.aggregate([
{
$lookup: {
from: "customers", // the collection to join with
localField: "customerId", // field in the orders collection
foreignField: "_id", // field in the customers collection
as: "customerDetails" // output field containing matched documents
}
}
])
✅ Result:
jsonCopyEdit[
{
"_id": 1,
"orderNumber": "ORD001",
"customerId": 101,
"amount": 300,
"customerDetails": [
{
"_id": 101,
"name": "Alice",
"email": "alice@example.com"
}
]
},
{
"_id": 2,
"orderNumber": "ORD002",
"customerId": 102,
"amount": 500,
"customerDetails": [
{
"_id": 102,
"name": "Bob",
"email": "bob@example.com"
}
]
}
]
🧠 Real-Life Use Cases
Join
orders
withproducts
,users
, orpayments
Combine blogs with authors
Combine invoices with clients
9. $addField
– Adds new field in document
$addField
is an aggregation stage to add new field in the document.
✅ Example: If you want to create a field fullname
then you can take firstname
and lastname
and combine them.
db.users.aggregate([
{
$addFields: {
fullName: { $concat: ["$firstName", " ", "$lastName"] }
}
}
])
10. $count
– count no. of documents
$addField
is an aggregation stage used to count total numbers of document.
✅ Example: Count how many users are adults.
db.users.aggregate([
{ $match: { age: { $gte: 18 } } },
{ $count: "adultCount" }
])
11. $facet
– Run multiple pipelines in parallel
$
facet is an aggregation stage used to run multiple pipelines parallel.
It allows you to run pipelines parallel so that you can get get multiple result on same time without querying multiple times through same document.
✅ Example: Get top 3 expensive and cheap products in a single query.
db.products.aggregate([
{
$facet: {
topExpensive: [{ $sort: { price: -1 } }, { $limit: 3 }],
topCheap: [{ $sort: { price: 1 } }, { $limit: 3 }]
}
}
])
11. $merge
– Write Results to Another Collection
This aggregation stage is used to write the result which you got from multiple pipelines and save the results of your aggregation into a different collection.
✅ Example: Save summary result into salesSummary
collection.
db.sales.aggregate([
{
$group: {
_id: "$region",
totalSales: { $sum: "$amount" }
}
},
{ $merge: "salesSummary" }
])
13. $replaceRoot
– write aggregation result to another collection
$replaceRoot
is an aggregation stage in MongoDB that replaces the entire document (the root) with a specified sub-document.
✅ In short:
It promotes a nested object (like a field inside the document) to become the main/root document.
🔧 Syntax:
jsCopyEdit{
$replaceRoot: {
newRoot: <expression> // usually a field like "$profile"
}
}
🧾 Example:
Suppose you have the following document in your users
collection:
jsonCopyEdit{
"_id": 1,
"name": "Ravi",
"profile": {
"age": 25,
"country": "India"
}
}
💡 Aggregation Query:
jsCopyEditdb.users.aggregate([
{ $replaceRoot: { newRoot: "$profile" } }
])
✅ Output:
jsonCopyEdit{
"age": 25,
"country": "India"
}
🧠 Now the profile
object becomes the root document — all other fields like _id
and name
are removed.
✅ Use Cases
Use Case | Stages Used |
Customer spending summary | $match , $group , $sort |
Paginated search results | $match , $sort , $skip , $limit |
Flattening nested documents | $unwind , $replaceRoot |
Joining customer details with orders | $lookup , $unwind , $project |
Reporting top/bottom records together | $facet , $sort , $limit |
Saving result to another collection | $group , $merge |
📌 Final Thoughts
The Aggregation Pipeline is not just a query tool—it's a mini data-processing engine inside MongoDB. Once you master it, you can do most of your reporting, transformations, and data analysis without writing complex backend code.
Subscribe to my newsletter
Read articles from Ansh Sachan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ansh Sachan
Ansh Sachan
💻 Full-Stack Developer | 🚀 Building scalable apps with MERN & MongoDB Aggregations 🎯 Writing clean code, sharing dev journeys, and simplifying backend concepts 📚 Always learning | 🛠️ Indie builder | 💡 Teaching what I learn