Accumulator Operators
Aggregate across documents — sum, average, min, max, and collect values.
← Previous | Index | Next: 14 - Variable and Reference Expressions →
Accumulator operators are special: unlike other expressions, they have access to data from all documents in the current pipeline stage. They are primarily used inside $group and $bucket stages, but some (like $sum, $min, $max) also work on arrays inside $addFields and $project.
We’ll use the sales collection for these examples.
$addToSet and $push — Collect Values into Arrays
Both operators collect field values into an array during grouping.
| Operator | Behavior |
|---|---|
$addToSet | Collects unique values only (no duplicates) |
$push | Collects all values (duplicates preserved) |
{ $addToSet: <expression> }
{ $push: <expression> }Example: Collect items sold per category
db.sales.aggregate([
{
$group: {
_id: "$category",
itemsSold: { $addToSet: "$item" }
}
},
{ $out: "salesReport" }
]);Output:
{ _id: "BOARD_GAME", itemsSold: ["abc1", "abc3"] }
{ _id: "BOOK", itemsSold: ["xyz", "abc2"] }
{ _id: "PC_GAME", itemsSold: ["jkl"] }$min, $max, $avg — Statistical Aggregates
Compute the minimum, maximum, or average across all documents in a group.
{ $min: <expression> }
{ $max: <expression> }
{ $avg: <expression> }Example: Price statistics per category
db.sales.aggregate([
{
$group: {
_id: "$category",
minPrice: { $min: "$price" },
maxPrice: { $max: "$price" },
avgPrice: { $avg: "$price" }
}
},
{ $sort: { _id: 1 } },
{ $out: "salesReport" }
]);Output:
{ _id: "BOARD_GAME", minPrice: 10, maxPrice: 10, avgPrice: 10 }
{ _id: "BOOK", minPrice: 5, maxPrice: 10, avgPrice: 7.5 }
{ _id: "PC_GAME", minPrice: 20, maxPrice: 20, avgPrice: 20 }On arrays vs. groups: When used inside
$addFieldsor$project, these operators work on array fields within a single document. When used inside$group, they aggregate across all documents in the group.
$sum — Sum Values or Count Documents
$sum has two common uses:
- Sum a field’s values across grouped documents:
{ $sum: "$fieldName" } - Count documents in each group:
{ $sum: 1 }
{ $sum: <expression> }Example: Count projects and collect titles by type
db.projects.aggregate([
{
$match: {
state: { $ne: "IN_APPROVEMENT" },
type: { $ne: "STIPENDIUM" }
}
},
{
$group: {
_id: "$type",
projectCount: { $sum: 1 },
projects: { $addToSet: "$title" }
}
},
{ $out: "projectReport" }
]);Output:
{
_id: "REQUEST_PROJECT",
projectCount: 1,
projects: ["Production Planning Systems"]
}Accumulators in Context: $group vs $addFields
Understanding where accumulators work is important:
┌──────────────────────────────────────────────────────────┐
│ $group stage │
│ │
│ Accumulators work ACROSS documents: │
│ │
│ doc1.price ─┐ │
│ doc2.price ─┤──▶ { $sum: "$price" } ──▶ total: 45 │
│ doc3.price ─┘ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ $addFields stage │
│ │
│ Accumulators work on ARRAYS within one document: │
│ │
│ reviews: [4,5,4,5,5] ──▶ { $sum: "$reviews" } ──▶ 23 │
└──────────────────────────────────────────────────────────┘
Quick Reference
| Operator | In $group | In $addFields/$project |
|---|---|---|
$sum | Sum field across group, or count with $sum: 1 | Sum array elements |
$avg | Average across group | Average of array |
$min | Min across group | Min of array |
$max | Max across group | Max of array |
$push | Collect all values into array | N/A |
$addToSet | Collect unique values into array | N/A |