MongoDB aggregation pipelines

Ansh SachanAnsh Sachan
7 min read

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.

MongoDB Aggregation: tutorial with examples and exercises | Studio 3T

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. $sortSorting 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. $limitLimit 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. $skipSkip 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 name

  • localField - the field in the current (input) collection

  • foreignField - the field in the foreign collection

  • as - 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 with products, users, or payments

  • 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 CaseStages 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.

10
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