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.

OperatorBehavior
$addToSetCollects unique values only (no duplicates)
$pushCollects 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 $addFields or $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:

  1. Sum a field’s values across grouped documents: { $sum: "$fieldName" }
  2. 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

OperatorIn $groupIn $addFields/$project
$sumSum field across group, or count with $sum: 1Sum array elements
$avgAverage across groupAverage of array
$minMin across groupMin of array
$maxMax across groupMax of array
$pushCollect all values into arrayN/A
$addToSetCollect unique values into arrayN/A

Next: 14 - Variable and Reference Expressions