β‘ Aggregation Framework
Master data transformation pipelines with powerful aggregation stages, live console, and real-world analytics
π The Tale of the Data Factory
Imagine you own a juice factory. Raw fruits come in, and finished juice bottles go out. But in between, there's a whole assembly line:
π Stage 1: WASH (Filter out bad fruits)
π Stage 2: PEEL (Remove unwanted parts)
π Stage 3: JUICE (Extract the good stuff)
π Stage 4: MIX (Combine with other ingredients)
π Stage 5: BOTTLE (Package the final product)
π Stage 6: COUNT (How many bottles per flavor?)
That's exactly what MongoDB's Aggregation Pipeline does with your data!
π Stage 1: $match - Filter documents (like washing fruits)
π Stage 2: $project - Select fields (like peeling)
π Stage 3: $group - Calculate totals (like mixing)
π Stage 4: $sort - Order results (like organizing bottles)
π Stage 5: $limit - Take top N (like selecting best batches)
π Stage 6: $lookup - Join collections (like adding labels)
β Without Aggregation: You fetch ALL documents, process them in your application code (slow, memory-intensive, inefficient!)
β With Aggregation: MongoDB does the heavy lifting on the database server, sends you only the results you need (fast, efficient, scalable!)
π‘ That's the Aggregation Framework!
A pipeline of stages that transform your data step-by-step.
Each stage does one thing, passes results to the next stage.
Perfect for analytics, reports, dashboards, and complex queries! π
β‘ What is the Aggregation Framework?
The Aggregation Framework is MongoDB's powerful data processing pipeline. It lets you filter, transform, group, sort, and analyze documents using a series of stages. Think of it as SQL's GROUP BY, JOIN, and HAVING on steroids!
Pipeline Concept
An aggregation pipeline consists of one or more stages. Each stage transforms the documents as they pass through. The output of one stage becomes the input of the next.
db.collection.aggregate([
{ $match: { status: "active" } }, // Stage 1: Filter
{ $group: { _id: "$city", count: { $sum: 1 } } }, // Stage 2: Group
{ $sort: { count: -1 } }, // Stage 3: Sort
{ $limit: 10 } // Stage 4: Limit
])
Key Benefits:
- π Server-side processing (faster than client-side)
- πͺ Handles complex transformations
- π Perfect for analytics and reporting
- π Can join multiple collections ($lookup)
- β‘ Optimized by MongoDB query planner
π§ Common Pipeline Stages
MongoDB offers 30+ aggregation stages. Here are the most important ones you'll use daily:
$match
Filters documents (like find()). Use early in pipeline for performance!
$project
Reshapes documents - include/exclude fields, create computed fields.
$group
Groups documents by field and performs calculations (sum, avg, count).
$sort
Orders documents by field(s). 1 = ascending, -1 = descending.
$limit
Limits the number of documents passed to next stage.
$skip
Skips first N documents. Useful for pagination.
$lookup
Performs a left outer join with another collection (like SQL JOIN).
$unwind
Deconstructs an array field, creating one document per array element.
$count
Counts the number of documents and returns the count.
$addFields
Adds new computed fields to documents without removing existing fields.
$bucket
Categorizes documents into buckets based on field values.
$facet
Processes multiple pipelines in parallel within a single stage.
π» Live Aggregation Console
Try aggregation pipelines on real data! Click any example below or write your own pipeline.
Sample Sales Data (12 documents)
π Pipeline Execution Flow
Watch how documents flow through each stage of your pipeline
π― Common Aggregation Patterns
Here are the most frequently used aggregation patterns in real applications:
Pattern 1: Count by Category
How many documents in each category? Perfect for dashboards and analytics.
// Count sales by product
db.sales.aggregate([
{ $group: {
_id: "$product",
count: { $sum: 1 },
total: { $sum: "$amount" }
}},
{ $sort: { total: -1 } }
])
// Result: { _id: "Laptop", count: 3, total: 3600 }
Pattern 2: Sum/Average Calculations
Calculate totals, averages, min, max for numerical fields.
// Total and average sales by month
db.sales.aggregate([
{ $group: {
_id: { $month: "$date" },
totalSales: { $sum: "$amount" },
avgSale: { $avg: "$amount" },
minSale: { $min: "$amount" },
maxSale: { $max: "$amount" },
transactions: { $sum: 1 }
}},
{ $sort: { _id: 1 } }
])
// Result: { _id: 1, totalSales: 5500, avgSale: 916.67, ... }
Pattern 3: Filter β Group β Sort
The most common pipeline: filter first, then aggregate, finally sort.
// Top 5 customers by completed orders
db.orders.aggregate([
{ $match: { status: "completed" } }, // Filter
{ $group: {
_id: "$customerId",
totalSpent: { $sum: "$amount" },
orderCount: { $sum: 1 }
}},
{ $sort: { totalSpent: -1 } }, // Sort
{ $limit: 5 } // Top 5
])
// Performance tip: Always $match early to reduce documents!
Pattern 4: $lookup (Join Collections)
Join data from multiple collections like SQL JOIN.
// Get orders with customer details
db.orders.aggregate([
{ $lookup: {
from: "customers", // Join with customers collection
localField: "customerId", // Field in orders
foreignField: "_id", // Field in customers
as: "customerInfo" // Output array name
}},
{ $unwind: "$customerInfo" }, // Convert array to object
{ $project: {
orderDate: 1,
amount: 1,
customerName: "$customerInfo.name",
customerEmail: "$customerInfo.email"
}}
])
// Result: { orderDate: ..., amount: 100, customerName: "Alice", ... }
Pattern 5: $unwind Arrays
Break down array fields into separate documents for analysis.
// Document: { name: "Alice", tags: ["VIP", "Premium", "Loyalty"] }
db.users.aggregate([
{ $unwind: "$tags" }, // Creates 3 documents, one per tag
{ $group: {
_id: "$tags",
userCount: { $sum: 1 }
}},
{ $sort: { userCount: -1 } }
])
// Result: { _id: "VIP", userCount: 15 }
// { _id: "Premium", userCount: 8 }
// { _id: "Loyalty", userCount: 12 }
Pattern 6: Date Aggregations
Group by date parts (year, month, day) for time-series analysis.
// Sales by year and month
db.sales.aggregate([
{ $group: {
_id: {
year: { $year: "$date" },
month: { $month: "$date" }
},
totalSales: { $sum: "$amount" },
avgSale: { $avg: "$amount" }
}},
{ $sort: { "_id.year": 1, "_id.month": 1 } }
])
// Result: { _id: { year: 2024, month: 1 }, totalSales: 15000, ... }
π Real-World Scenarios
See how aggregation solves real business problems:
1. E-Commerce Sales Dashboard
Requirement: Show total sales, top products, and revenue trends
// Complete dashboard query
db.orders.aggregate([
// Only completed orders
{ $match: { status: "completed" } },
// Add year-month field
{ $addFields: {
yearMonth: {
$dateToString: { format: "%Y-%m", date: "$orderDate" }
}
}},
// Group by time and product
{ $group: {
_id: {
period: "$yearMonth",
product: "$productName"
},
revenue: { $sum: "$amount" },
quantity: { $sum: "$quantity" },
orderCount: { $sum: 1 }
}},
// Sort by period and revenue
{ $sort: { "_id.period": -1, "revenue": -1 } },
// Format output
{ $project: {
_id: 0,
period: "$_id.period",
product: "$_id.product",
revenue: 1,
quantity: 1,
orders: "$orderCount",
avgOrderValue: { $divide: ["$revenue", "$orderCount"] }
}}
])
// Perfect for charts and reports!
2. Customer Segmentation
Requirement: Categorize customers into VIP, Regular, Inactive
// Segment customers by purchase behavior
db.customers.aggregate([
// Join with orders
{ $lookup: {
from: "orders",
localField: "_id",
foreignField: "customerId",
as: "orders"
}},
// Calculate metrics
{ $addFields: {
totalSpent: { $sum: "$orders.amount" },
orderCount: { $size: "$orders" },
lastOrderDate: { $max: "$orders.orderDate" }
}},
// Categorize customers
{ $addFields: {
segment: {
$switch: {
branches: [
{ case: { $gte: ["$totalSpent", 5000] }, then: "VIP" },
{ case: { $gte: ["$totalSpent", 1000] }, then: "Regular" },
{ case: { $lt: ["$orderCount", 1] }, then: "Inactive" }
],
default: "New"
}
}
}},
// Group by segment
{ $group: {
_id: "$segment",
customerCount: { $sum: 1 },
totalRevenue: { $sum: "$totalSpent" },
avgSpent: { $avg: "$totalSpent" }
}},
{ $sort: { totalRevenue: -1 } }
])
// Result: { _id: "VIP", customerCount: 42, totalRevenue: 350000, ... }
3. Product Performance Report
Requirement: Which products are selling well? Which are slow?
// Comprehensive product analysis
db.orders.aggregate([
// Last 90 days only
{ $match: {
orderDate: { $gte: new Date(Date.now() - 90*24*60*60*1000) }
}},
// Unwind line items
{ $unwind: "$items" },
// Group by product
{ $group: {
_id: "$items.productId",
productName: { $first: "$items.productName" },
unitsSold: { $sum: "$items.quantity" },
revenue: { $sum: { $multiply: ["$items.quantity", "$items.price"] } },
orderCount: { $sum: 1 },
avgPrice: { $avg: "$items.price" }
}},
// Calculate metrics
{ $addFields: {
revenuePerUnit: { $divide: ["$revenue", "$unitsSold"] }
}},
// Categorize performance
{ $addFields: {
performance: {
$cond: {
if: { $gte: ["$unitsSold", 100] },
then: "Hot Seller",
else: { $cond: {
if: { $gte: ["$unitsSold", 20] },
then: "Steady",
else: "Slow Mover"
}}
}
}
}},
{ $sort: { revenue: -1 } }
])
// Use this for inventory decisions!
4. Leaderboard with Rankings
Requirement: Show top users with ranks and percentiles
// Gaming leaderboard with rankings
db.users.aggregate([
// Active users only
{ $match: { status: "active" } },
// Sort by score
{ $sort: { score: -1, level: -1 } },
// Add rank using $setWindowFields (MongoDB 5.0+)
{ $setWindowFields: {
sortBy: { score: -1, level: -1 },
output: {
rank: { $rank: {} },
percentile: { $percentile: { input: "$score", p: [0.5], method: "approximate" } }
}
}},
// Add rank labels
{ $addFields: {
rankLabel: {
$switch: {
branches: [
{ case: { $lte: ["$rank", 10] }, then: "π₯ Top 10" },
{ case: { $lte: ["$rank", 100] }, then: "π₯ Top 100" },
{ case: { $lte: ["$rank", 1000] }, then: "π₯ Top 1000" }
],
default: "Player"
}
}
}},
// Top 50 only
{ $limit: 50 },
// Clean output
{ $project: {
_id: 0,
rank: 1,
username: 1,
score: 1,
level: 1,
rankLabel: 1
}}
])
// Perfect for competitive games!
πΌ Interview Questions & Answers
Master these 6 aggregation questions to ace your MongoDB interview: