MongoDB $group Accumulators: $sum, $avg, $min, $max, $push & $addToSet
Learn every MongoDB $group accumulator operator — $sum, $count, $avg, $min, $max, $first, $last, $push and $addToSet — with real aggregation examples.
$group Accumulators
Accumulators are the expressions you place inside $group to actually compute something for each group — sums, averages, extremes, and even arrays of the grouped values.
| Accumulator | Description |
|---|---|
$sum |
Adds up numeric values across a group |
$count |
Counts documents in a group |
$avg |
Averages a field's value across a group |
$min / $max |
Finds the smallest / largest value |
$first / $last |
Grabs the first / last document's value in the group |
$push |
Collects values into an array (allows duplicates) |
$addToSet |
Collects values into an array (no duplicates) |
$sum
db.employees.aggregate([
{ $group: { _id: "$city", countcity: { $sum: 1 } } }
])
// $sum: 1 is the classic "count per group" pattern
db.employees.aggregate([
{ $match: { salary: { $gte: 50000 } } },
{ $group: { _id: "$age", countcity: { $sum: 1 } } }
])
$count
db.employees.aggregate([{ $count: "age" }])
// { age: 6 } -- top-level count of all documents (labeled "age" here, name is arbitrary)
db.employees.aggregate([
{ $group: { _id: "$age", count: { $count: {} } } }
])
// { _id: 24, count: 1 }
// { _id: 22, count: 2 }
$avg
db.employees.aggregate([
{ $group: { _id: "$age", avgsalary: { $avg: "$salary" } } },
{ $sort: { avgsalary: -1 } }
])
db.employees.aggregate([
{ $group: { _id: "$age", avgsalary: { $avg: "$salary" } } },
{ $match: { avgsalary: { $gte: 50000 } } },
{ $sort: { avgsalary: -1 } }
])
// you CAN $match again after a $group, to filter on the computed values
Advertisement728 x 90
$min / $max
db.employees.aggregate([
{ $group: { _id: null, minsalary: { $min: "$salary" } } }
])
// _id: null groups the ENTIRE collection into one group
db.employees.aggregate([
{ $group: { _id: null, maxsalary: { $max: "$salary" } } },
{ $project: { _id: 0 } }
])
$first / $last
Returns the first (or last) document's value within the order the pipeline received them — usually paired with $match to scope things down first.
db.employees.aggregate([
{ $match: { city: "Mumbai" } },
{ $group: { _id: null, first: { $first: "$age" } } }
])
db.employees.aggregate([
{ $match: { city: "Mumbai" } },
{ $group: { _id: null, last: { $last: "$age" } } }
])
$push — Collect Into an Array (With Duplicates)
db.employees.aggregate([
{
$group: {
_id: null,
basicdetail: { $push: { name: "$name", email: "$email" } }
}
}
])
// builds one array containing a {name, email} object per document
$addToSet — Collect Into an Array (No Duplicates)
db.employees.aggregate([
{ $group: { _id: null, city: { $addToSet: "$city" } } }
])
// { _id: null, city: [ 'Mumbai', 'Palghar', 'Pune' ] }
Common Errors & Troubleshooting
$sum/$avgproducing0/nullunexpectedly — check the field actually holds a number in every document; a string value in a numeric field is silently ignored by these accumulators rather than causing an error.$first/$lastreturning inconsistent results across runs — without a$sortstage before$group, the order documents arrive in isn't guaranteed — always sort first if you depend on which document is "first" or "last."$pushblowing past the 16MB document size limit — if a group has an enormous number of documents, pushing every one of them into a single array can exceed MongoDB's document size limit; consider$limiting what you collect, or restructuring the query.
Key Takeaways
_id: nullinside$grouptreats the whole collection as a single group — perfect for overall totals/averages.- You can
$matchagain after a$groupto filter on the computed accumulator values. $pushkeeps duplicates;$addToSetdoesn't — pick based on whether you need a "list" or a "set".
Advertisement320 x 100