MongoDB Aggregation Guide

Pipeline Stage Reference

StagePurposeNotes
$matchFilter documents (like WHERE)Put early to use indexes
$groupGroup + accumulate (_id required)$sum, $avg, $min, $max, $push, $addToSet
$projectReshape output fields1=include, 0=exclude, expressions allowed
$sortOrder results1=asc, -1=desc; index-backed before $group
$limit / $skipPaginationUse together; $skip is expensive at scale
$lookupLeft outer join to another collectionUse $match pipeline variant for conditions
$unwindDeconstruct array field into separate docspreserveNullAndEmptyArrays option
$addFieldsAdd/overwrite fields without changing othersAlias: $set
$facetMultiple pipelines in one passGood for search + count + facets
$bucketCategorize into ranges$bucketAuto for automatic ranges

Common Aggregation Patterns

// Sales report: total revenue by category per month db.orders.aggregate([ { $match: { status: "completed", createdAt: { $gte: ISODate("2024-01-01") } } }, { $unwind: "$items" }, { $group: { _id: { month: { $month: "$createdAt" }, category: "$items.category" }, revenue: { $sum: { $multiply: ["$items.price", "$items.qty"] } }, orderCount: { $sum: 1 } }}, { $sort: { "_id.month": 1, revenue: -1 } }, { $project: { _id: 0, month: "$_id.month", category: "$_id.category", revenue: { $round: ["$revenue", 2] }, orderCount: 1 }} ]); // $lookup with pipeline (join on multiple conditions) db.orders.aggregate([ { $lookup: { from: "products", let: { productId: "$productId", minQty: "$quantity" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$_id", "$$productId"] }, { $gt: ["$stock", "$$minQty"] } ] }}}, { $project: { name: 1, price: 1 } } ], as: "productDetails" }}, { $unwind: { path: "$productDetails", preserveNullAndEmptyArrays: true } } ]); // Faceted search (results + counts + price ranges in one query) db.products.aggregate([ { $match: { $text: { $search: "laptop" } } }, { $facet: { results: [{ $sort: { score: -1 } }, { $limit: 20 }], totalCount: [{ $count: "count" }], byBrand: [{ $group: { _id: "$brand", count: { $sum: 1 } } }], priceRanges: [{ $bucket: { groupBy: "$price", boundaries: [0, 500, 1000, 2000, 5000], default: "5000+", output: { count: { $sum: 1 } } }}] }} ]);

Indexes for Aggregation

// Always $match early to leverage indexes // These indexes support common aggregation patterns: // Compound index for filtered aggregations db.orders.createIndex({ status: 1, createdAt: -1, userId: 1 }); // Text index for $text/$search db.products.createIndex({ name: "text", description: "text" }); // Explain aggregation pipeline db.orders.explain("executionStats").aggregate([ { $match: { status: "pending" } }, { $group: { _id: "$userId", total: { $sum: "$amount" } } } ]); // Allow disk use for large aggregations (> 100MB) db.orders.aggregate([...], { allowDiskUse: true });