Playbook

01. REST API Design Principles02. HTTP Methods and Status Co...03. Backend Fundamentals: 0104. Backend Fundamentals: 0205. GitHub OAuth in Node.js Ap...06. Google OAuth in Node.js Ap...07. MongoDB Aggregation08. Introduction to YAML09. Basics Concepts of Normali...10. Setting Up ESLint, Commit...11. Replication and Sharding12. Introduction to Redis13. Introduction to Kafka
Profile
Akkal DhamiFull Stack Developer

Building modern web experiences with a focus on performance, scalability, and clean architecture.

© 2026 | Akkal Dhami | All rights reserved

Built with
byAkkal Dhami

Navigation

  • Projects
  • Dev Setup
  • Playbook
  • Templates
  • Networking
  • SQL - MySQL
  • SQL Playground
  • DSA
AKKAL DHAMIAKKAL DHAMIAKKAL DHAMI

MongoDB Aggregation

1. What is MongoDB Aggregation?

MongoDB aggregation is a way to process and transform data inside the database.

Think of it like a pipeline:

Documents → Step 1 → Step 2 → Step 3 → Result

Each step modifies, filters, groups, or reshapes the data.

MongoDB uses the Aggregation Pipeline:

db.collection.aggregate([
  { stage1 },
  { stage2 },
  { stage3 }
])

2. Why Use Aggregation?

Aggregation helps you:

  • Filter data
  • Group documents
  • Calculate totals/averages
  • Join collections
  • Transform fields
  • Sort & paginate
  • Build analytics

Similar to:

  • SQL GROUP BY
  • SQL JOIN
  • SQL calculations

3. Sample Data

We'll use this sample data to demonstrate aggregation stages:

products.js
[
  {
    _id: 101,
    name: "iPhone 15",
    category: "Mobile",
    brand: "Apple",
    price: 85000,
    stock: 12,
    tags: ["electronics", "phone", "ios"],
    ratings: [5, 4, 5, 3, 4],
    createdAt: new Date("2025-01-10")
  },
  {
    _id: 102,
    name: "Samsung S24",
    category: "Mobile",
    brand: "Samsung",
    price: 72000,
    stock: 20,
    tags: ["android", "electronics"],
    ratings: [4, 4, 5],
    createdAt: new Date("2025-02-11")
  },
  {
    _id: 103,
    name: "MacBook Air",
    category: "Laptop",
    brand: "Apple",
    price: 120000,
    stock: 7,
    tags: ["laptop", "macos"],
    ratings: [5, 5, 5],
    createdAt: new Date("2024-09-01")
  },
  {
    _id: 104,
    name: "Boat Headphones",
    category: "Accessories",
    brand: "Boat",
    price: 2500,
    stock: 50,
    tags: ["audio", "music"],
    ratings: [3, 4, 4],
    createdAt: new Date("2025-03-14")
  },
  {
    _id: 105,
    name: "Gaming Mouse",
    category: "Accessories",
    brand: "Logitech",
    price: 1800,
    stock: 35,
    tags: ["gaming", "pc"],
    ratings: [5, 4, 4, 5],
    createdAt: new Date("2025-01-20")
  }
];
orders.js
[
  {
    _id: 1001,
    userId: 1,
    products: [
      {
        productId: 101,
        quantity: 1
      },
      {
        productId: 104,
        quantity: 2
      }
    ],
    totalAmount: 90000,
    status: "DELIVERED",
    paymentMethod: "UPI",
    shippingAddress: {
      city: "Bhagalpur",
      state: "Bihar"
    },
    orderDate: new Date("2025-04-01")
  },
  {
    _id: 1002,
    userId: 2,
    products: [
      {
        productId: 102,
        quantity: 1
      }
    ],
    totalAmount: 72000,
    status: "PENDING",
    paymentMethod: "Card",
    shippingAddress: {
      city: "Patna",
      state: "Bihar"
    },
    orderDate: new Date("2025-04-05")
  },
  {
    _id: 1003,
    userId: 3,
    products: [
      {
        productId: 103,
        quantity: 1
      },
      {
        productId: 105,
        quantity: 2
      }
    ],
    totalAmount: 123600,
    status: "DELIVERED",
    paymentMethod: "NetBanking",
    shippingAddress: {
      city: "Delhi",
      state: "Delhi"
    },
    orderDate: new Date("2025-04-10")
  },
  {
    _id: 1004,
    userId: 1,
    products: [
      {
        productId: 105,
        quantity: 3
      }
    ],
    totalAmount: 5400,
    status: "CANCELLED",
    paymentMethod: "COD",
    shippingAddress: {
      city: "Bhagalpur",
      state: "Bihar"
    },
    orderDate: new Date("2025-04-15")
  },
  {
    _id: 1011,
    userId: 2,
    products: [
      {
        productId: 101,
        quantity: 1
      },
      {
        productId: 104,
        quantity: 2
      }
    ],
    totalAmount: 90000,
    status: "DELIVERED",
    paymentMethod: "UPI",
    shippingAddress: {
      city: "Patna",
      state: "Bihar"
    },
    orderDate: new Date()
  },
  {
    _id: 1012,
    userId: 1,
    products: [
      {
        productId: 103,
        quantity: 1
      }
    ],
    totalAmount: 120000,
    status: "DELIVERED",
    paymentMethod: "Card",
    shippingAddress: {
      city: "Bhagalpur",
      state: "Bihar"
    },
    orderDate: new Date(Date.now() - 5 * 60 * 60 * 1000)
  },
  {
    _id: 1013,
    userId: 3,
    products: [
      {
        productId: 105,
        quantity: 3
      }
    ],
    totalAmount: 5400,
    status: "PENDING",
    paymentMethod: "COD",
    shippingAddress: {
      city: "Delhi",
      state: "Delhi"
    },
    orderDate: new Date(Date.now() - 10 * 60 * 60 * 1000)
  }
];

4. Most Important Aggregation Stages

4.1. $match : Filter Documents

Like SQL WHERE.

$match is used to:

Filter documents based on a condition.

1. Filter pending orders.
db.orders.aggregate([
  {
    $match: {
      status: "PENDING"
    }
  }
]);

Output:

[
  {
    _id: 1002,
    userId: 2,
    products: [ { productId: 102, quantity: 1 } ],
    totalAmount: 72000,
    status: 'PENDING',
    paymentMethod: 'Card',
    shippingAddress: { city: 'Patna', state: 'Bihar' },
    orderDate: ISODate('2025-04-05T00:00:00.000Z')
  },
  {
    _id: 1013,
    userId: 3,
    products: [ { productId: 105, quantity: 3 } ],
    totalAmount: 5400,
    status: 'PENDING',
    paymentMethod: 'COD',
    shippingAddress: { city: 'Delhi', state: 'Delhi' },
    orderDate: ISODate('2026-05-27T01:30:29.284Z')
  }
]

2. Filter all products whose price is greater than 10000.
db.products.aggregate([
  {
    $match: {
      price: {
        $gt: 10000
      }
    }
  }
]);

Output:

[
  {
    _id: 101,
    name: "iPhone 15",
    category: "Mobile",
    brand: "Apple",
    price: 85000,
    stock: 12,
    tags: ["electronics", "phone", "ios"],
    ratings: [5, 4, 5, 3, 4],
    createdAt: ISODate("2025-01-10T00:00:00.000Z")
  },
  {
    _id: 102,
    name: "Samsung S24",
    category: "Mobile",
    brand: "Samsung",
    price: 72000,
    stock: 20,
    tags: ["android", "electronics"],
    ratings: [4, 4, 5],
    createdAt: ISODate("2025-02-11T00:00:00.000Z")
  },
  {
    _id: 103,
    name: "MacBook Air",
    category: "Laptop",
    brand: "Apple",
    price: 120000,
    stock: 7,
    tags: ["laptop", "macos"],
    ratings: [5, 5, 5],
    createdAt: ISODate("2024-09-01T00:00:00.000Z")
  }
];

4.2 $group : Group Documents

Like SQL GROUP BY.

$group is used to:

Group documents by a common field and apply an aggregation operation to each group.

_id means: Group by this field Not the actual MongoDB document ID.

1. Count how many products are in each category.
db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalProducts: {
        $sum: 1
      },
      products: {
        $push: {
          _id: "$_id",
          name: "$name",
          price: "$price"
        }
      }
    }
  }
]);

Output:

[
  {
    _id: "Accessories",
    totalProducts: 2,
    products: [
      {
        _id: 104,
        name: "Boat Headphones",
        price: 2500
      },
      {
        _id: 105,
        name: "Gaming Mouse",
        price: 1800
      }
    ]
  },
  {
    _id: "Laptop",
    totalProducts: 1,
    products: [
      {
        _id: 103,
        name: "MacBook Air",
        price: 120000
      }
    ]
  },
  {
    _id: "Mobile",
    totalProducts: 2,
    products: [
      {
        _id: 101,
        name: "iPhone 15",
        price: 85000
      },
      {
        _id: 102,
        name: "Samsung S24",
        price: 72000
      }
    ]
  }
];

2. Count total stock & products per category.
db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalProducts: {
        $sum: 1
      },
      totalStock: {
        $sum: "$stock"
      }
    }
  }
]);

Output:

[
  {
    _id: "Mobile",
    totalProducts: 2,
    totalStock: 32
  },
  {
    _id: "Laptop",
    totalProducts: 1,
    totalStock: 7
  },
  {
    _id: "Accessories",
    totalProducts: 2,
    totalStock: 85
  }
];

3. Count total price per category.
db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: {
        $sum: "$price"
      }
    }
  }
]);

Output:

[
  { _id: "Mobile", totalPrice: 157000 },
  { _id: "Laptop", totalPrice: 120000 },
  { _id: "Accessories", totalPrice: 4300 }
];

4.3 $project : Select/Transform Fields

$project is used to:

Select or transform fields in the output documents.

db.products.aggregate([
  {
    $match: {
      brand: "Apple"
    }
  },
  {
    $project: {
      name: 1,
      price: 1,
      category: 1,
      brand: 1
    }
  }
]);

Output:

[
  {
    _id: 101,
    name: 'iPhone 15',
    category: 'Mobile',
    brand: 'Apple',
    price: 85000
  },
  {
    _id: 103,
    name: 'MacBook Air',
    category: 'Laptop',
    brand: 'Apple',
    price: 120000
  }
]

4.4 $sort : Sort Documents

$sort is used to:

Sort documents in ascending or descending order.

db.products.aggregate([
  {
    $match: {
      brand: "Apple"
    }
  },
  {
    $project: {
      name: 1,
      price: 1,
      category: 1,
      brand: 1
    }
  },
  {
    $sort: {
      price: 1
    }
  }
]);
  • 1 : sort by ascending order.
  • -1 : sort by descending order.

Output:

[
  {
    _id: 101,
    name: 'iPhone 15',
    category: 'Mobile',
    brand: 'Apple',
    price: 85000
  },
  {
    _id: 103,
    name: 'MacBook Air',
    category: 'Laptop',
    brand: 'Apple',
    price: 120000
  }
]

4.5 $limit : Limit Documents

$limit is used to:

Limit the number of documents returned.

db.products.aggregate([
  {
    $limit: 3
  }
]);

Output:

[
  {
    _id: 101,
    name: "iPhone 15",
    category: "Mobile",
    brand: "Apple",
    price: 85000,
    stock: 12,
    tags: ["electronics", "phone", "ios"],
    ratings: [5, 4, 5, 3, 4],
    createdAt: ISODate("2025-01-10T00:00:00.000Z")
  },
  {
    _id: 102,
    name: "Samsung S24",
    category: "Mobile",
    brand: "Samsung",
    price: 72000,
    stock: 20,
    tags: ["android", "electronics"],
    ratings: [4, 4, 5],
    createdAt: ISODate("2025-02-11T00:00:00.000Z")
  },
  {
    _id: 103,
    name: "MacBook Air",
    category: "Laptop",
    brand: "Apple",
    price: 120000,
    stock: 7,
    tags: ["laptop", "macos"],
    ratings: [5, 5, 5],
    createdAt: ISODate("2024-09-01T00:00:00.000Z")
  }
];

4.6 $skip : Skip Documents

$skip is used to:

Skip documents before returning the first document. Useful for pagination.

db.products.aggregate([
  {
    $skip: 2
  }
]);

Output:

[
  {
    _id: 103,
    name: "MacBook Air",
    category: "Laptop",
    brand: "Apple",
    price: 120000,
    stock: 7,
    tags: ["laptop", "macos"],
    ratings: [5, 5, 5],
    createdAt: ISODate("2024-09-01T00:00:00.000Z")
  },
  {
    _id: 104,
    name: "Boat Headphones",
    category: "Accessories",
    brand: "Boat",
    price: 2500,
    stock: 50,
    tags: ["audio", "music"],
    ratings: [3, 4, 4],
    createdAt: ISODate("2025-03-14T00:00:00.000Z")
  },
  {
    _id: 105,
    name: "Gaming Mouse",
    category: "Accessories",
    brand: "Logitech",
    price: 1800,
    stock: 35,
    tags: ["gaming", "pc"],
    ratings: [5, 4, 4, 5],
    createdAt: ISODate("2025-01-20T00:00:00.000Z")
  }
];

4.7 $lookup : Join Collections

Like SQL JOIN.

$lookup is used to:

Join data from other collections.

1. Find two orders with products details.
db.orders.aggregate([
  {
    $lookup: {
      from: "products",
      localField: "products.productId",
      foreignField: "_id",
      as: "productDetails"
    }
  },
  {
    $limit: 2
  }
]);

Output:

[
  {
    _id: 1001,
    userId: 1,
    products: [
      { productId: 101, quantity: 1 },
      { productId: 104, quantity: 2 }
    ],
    totalAmount: 90000,
    status: "DELIVERED",
    paymentMethod: "UPI",
    shippingAddress: { city: "Bhagalpur", state: "Bihar" },
    orderDate: ISODate("2025-04-01T00:00:00.000Z"),
    productDetails: [
      {
        _id: 104,
        name: "Boat Headphones",
        category: "Accessories",
        brand: "Boat",
        price: 2500,
        stock: 50,
        tags: ["audio", "music"],
        ratings: [3, 4, 4],
        createdAt: ISODate("2025-03-14T00:00:00.000Z")
      },
      {
        _id: 101,
        name: "iPhone 15",
        category: "Mobile",
        brand: "Apple",
        price: 85000,
        stock: 12,
        tags: ["electronics", "phone", "ios"],
        ratings: [5, 4, 5, 3, 4],
        createdAt: ISODate("2025-01-10T00:00:00.000Z")
      }
    ]
  },
  {
    _id: 1002,
    userId: 2,
    products: [{ productId: 102, quantity: 1 }],
    totalAmount: 72000,
    status: "PENDING",
    paymentMethod: "Card",
    shippingAddress: { city: "Patna", state: "Bihar" },
    orderDate: ISODate("2025-04-05T00:00:00.000Z"),
    productDetails: [
      {
        _id: 102,
        name: "Samsung S24",
        category: "Mobile",
        brand: "Samsung",
        price: 72000,
        stock: 20,
        tags: ["android", "electronics"],
        ratings: [4, 4, 5],
        createdAt: ISODate("2025-02-11T00:00:00.000Z")
      }
    ]
  }
];

2. Find orders with products details(_id, name, price, brand).
db.orders.aggregate([
  {
    $lookup: {
      from: "products",
      localField: "products.productId",
      foreignField: "_id",
      pipeline: [
        {
          $project: {
            name: 1,
            price: 1,
            brand: 1
          }
        }
      ],
      as: "productDetails"
    }
  },
  {
    $limit: 2
  }
]);

Output:

[
  {
    _id: 1001,
    userId: 1,
    products: [
      {
        productId: 101,
        quantity: 1
      },
      { 
        productId: 104,
        quantity: 2 
      }
    ],
    totalAmount: 90000,
    status: "DELIVERED",
    paymentMethod: "UPI",
    shippingAddress: {
      city: "Bhagalpur",
      state: "Bihar"
    },
    orderDate: ISODate("2025-04-01T00:00:00.000Z"),
    productDetails: [
      {
        _id: 101,
        name: "iPhone 15",
        brand: "Apple",
        price: 85000
      },
      {
        _id: 104,
        name: "Boat Headphones",
        brand: "Boat",
        price: 2500
      }
    ]
  },
  {
    _id: 1002,
    userId: 2,
    products: [
      {
        productId: 102,
        quantity: 1
      }
    ],
    totalAmount: 72000,
    status: "PENDING",
    paymentMethod: "Card",
    shippingAddress: {
      city: "Patna",
      state: "Bihar"
    },
    orderDate: ISODate("2025-04-05T00:00:00.000Z"),
    productDetails: [
      {
        _id: 102,
        name: "Samsung S24",
        brand: "Samsung",
        price: 72000
      }
    ]
  }
];

4.8 $unwind : Unwind Arrays

$unwind is used to:

Break an array into separate documents.

1. Unwind ratings array.
db.products.aggregate([
  {
    $unwind: "$ratings"
  },
  {
    $project: {
      name: 1,
      ratings: 1
    }
  },
  {
    $limit: 4
  }
]);

Output:

[
  {
    _id: 101,
    name: "iPhone 15",
    ratings: 5
  },
  {
    _id: 101,
    name: "iPhone 15",
    ratings: 4
  },
  {
    _id: 101,
    name: "iPhone 15",
    ratings: 5
  },
  {
    _id: 101,
    name: "iPhone 15",
    ratings: 3
  }
];

2. Count How Many Times Each Rating Appears
db.products.aggregate([
  {
    $unwind: "$ratings"
  },
  {
    $group: {
      _id: "$ratings",
      total: {
        $sum: 1
      }
    }
  },
  {
    $sort: {
      _id: -1
    }
  }
]);

Output:

[
  { _id: 5, total: 8 },
  { _id: 4, total: 8 },
  { _id: 3, total: 2 }
];

5. Aggregation Pipeline Example

Find the total revenue generated by each category of products in the last 24 hours.

db.orders.aggregate([
  {
    $match: {
      // last 24 hours
      orderDate: {
        $gt: new Date(Date.now() - 24 * 60 * 60 * 1000)
      }
    }
  },
  {
    $project: {
      products: 1
    }
  },
  {
    $unwind: "$products"
  },
  {
    $lookup: {
      from: "products",
      localField: "products.productId",
      foreignField: "_id",
    
      as: "productDetails"
    }
  },
  {
    $unwind: "$productDetails"
  },
  {
    $group: {
      _id: "$productDetails.category",
      totalRevenue: {
        $sum: {
          $multiply: ["$products.quantity", "$productDetails.price"]
        }
      }
    }
  }
]);

Output:

[
  { _id: "Accessories", totalRevenue: 10400 },
  { _id: "Laptop", totalRevenue: 120000 },
  { _id: "Mobile", totalRevenue: 85000 }
];

6. MongoDB Aggregation Stages & Their Uses

StageUse
$matchFilter documents
$groupGroup documents and calculate values
$projectSelect, remove, rename, or transform fields
$sortSort documents
$limitLimit number of documents
$skipSkip documents
$unwindSplit array into multiple documents
$lookupJoin another collection
$addFieldsAdd new fields
$setSame as $addFields
$unsetRemove fields
$countCount total documents
$sampleGet random documents
$replaceRootReplace entire document
$replaceWithSame as $replaceRoot
$sortByCountGroup, count, and sort documents
$unionWithCombine multiple collections
$outSave aggregation result into collection
$geoNearFind nearby locations
06. Google OAuth in Node.js Applicatio...
08. Introduction to YAML