Aggregation for reporting dashboards in MongoDB - Time & Space Complexity
When building reporting dashboards, we often use aggregation to summarize data quickly.
We want to know how the time to get these summaries grows as the data grows.
Analyze the time complexity of the following aggregation pipeline.
db.sales.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$region", totalSales: { $sum: "$amount" } } },
{ $sort: { totalSales: -1 } }
])
This pipeline filters completed sales, groups them by region summing amounts, then sorts by total sales.
Look for repeated work inside the pipeline.
- Primary operation: Scanning each sale document once to filter and group.
- How many times: Once per document in the sales collection.
As the number of sales grows, the work grows too.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 document checks and group updates |
| 100 | About 100 document checks and group updates |
| 1000 | About 1000 document checks and group updates |
Pattern observation: The work grows roughly in direct proportion to the number of sales.
Time Complexity: O(n)
This means the time to run the aggregation grows linearly with the number of sales records.
[X] Wrong: "Grouping by region makes the query slower by the square of the data size."
[OK] Correct: Grouping just collects data as it scans once; it does not multiply work by data size again.
Understanding how aggregation scales helps you explain how dashboards stay fast even with lots of data.
"What if we added a $lookup stage to join with another collection? How would the time complexity change?"
Practice
$group stage in a MongoDB aggregation pipeline for reporting dashboards?Solution
Step 1: Understand the role of $group
The$groupstage collects documents into groups and calculates summary statistics like sums or averages.Step 2: Compare with other stages
$matchfilters,$sortorders, and$addFieldsadds fields, but only$groupaggregates data.Final Answer:
To combine documents and calculate summary values like totals or averages -> Option BQuick Check:
$group = summary aggregation [OK]
- Confusing $group with $match filtering
- Thinking $group sorts data
- Assuming $group adds fields without aggregation
Solution
Step 1: Identify the aggregation method
The correct method to start aggregation isaggregate()with an array of stages.Step 2: Check syntax correctness
db.collection.aggregate([{ $match: { status: 'active' } }])is valid; others use wrong methods or syntax.Final Answer:
db.collection.aggregate([{ $match: { status: 'active' } }]) -> Option AQuick Check:
aggregate() starts pipeline [OK]
- Using find() instead of aggregate() for pipelines
- Trying to use group() as a method
- Incorrect method names like aggregateMatch()
sales with documents like { product: 'A', quantity: 5 } and { product: 'B', quantity: 3 }, what will this aggregation return?db.sales.aggregate([
{ $group: { _id: '$product', totalQuantity: { $sum: '$quantity' } } },
{ $sort: { totalQuantity: -1 } }
])Solution
Step 1: Group by product and sum quantity
The$groupstage groups documents by product and sums their quantities: 'A' -> 5, 'B' -> 3.Step 2: Sort by totalQuantity descending
The$sortorders results by totalQuantity descending, so 'A' (5) comes before 'B' (3).Final Answer:
[{ _id: 'A', totalQuantity: 5 }, { _id: 'B', totalQuantity: 3 }] -> Option CQuick Check:
Group sums then sort desc = correct order [OK]
- Mixing up sort order ascending vs descending
- Expecting combined total instead of grouped sums
- Thinking syntax error occurs
db.orders.aggregate([
{ $match: { status: 'completed' } },
{ $group: { _id: '$customerId', total: { $sum: '$amount' } } },
{ $sort: { total: 1 } },
{ $project: { customerId: 1, total: 1 } }
])Solution
Step 1: Check fields after $group
After grouping by_id: '$customerId', the output fields are_idandtotal. The originalcustomerIdfield is renamed to_id.Step 2: Analyze $project stage
The $project tries to includecustomerId, which does not exist; it should use_idinstead.Final Answer:
The $project stage uses a field not present in the grouped output -> Option DQuick Check:
Use _id after $group, not original field [OK]
- Assuming original fields remain after $group
- Placing $sort before $group incorrectly
- Misusing $sum outside $group
Solution
Step 1: Group by customer and calculate average and count
Use$groupto get average order amount and count of orders per customer.Step 2: Filter customers with more than 3 orders
Apply$matchafter grouping to keep only customers where count > 3.Final Answer:
[{ $group: { _id: '$customerId', avgAmount: { $avg: '$amount' }, count: { $sum: 1 } } }, { $match: { count: { $gt: 3 } } }] -> Option AQuick Check:
Group then filter by count > 3 [OK]
- Filtering by count before grouping
- Filtering by avgAmount instead of count
- Missing count calculation in $group
Ready to prove it?
Pass the test and this pattern moves unknown
