MongoDB group Stage

The $group stage collapses many documents into one document per distinct key, letting you compute sums, averages, counts, and other aggregates for each group.

The Anatomy of $group

A $group stage always specifies an _id field, which defines the grouping key, plus any number of accumulator fields that compute a value across all documents sharing that key. If you set _id to null, every document in the pipeline collapses into a single group, which is useful for computing an overall total or average across an entire collection.

Grouping by a Single Field

db.orders.aggregate([
  { $group: {
      _id: "$customerId",
      totalSpent: { $sum: "$amount" },
      orderCount: { $sum: 1 }
  } }
])

Common Accumulator Operators

MongoDB ships with a full set of accumulator operators you can use inside $group. $sum adds numeric values across the group, $avg computes a mean, $min and $max find extremes, $push collects every value into an array, and $addToSet does the same but discards duplicates. You can combine several accumulators in the same $group stage to compute multiple statistics in a single pass.

  • $sum: adds a field's value, or increment by 1 for a plain count
  • $avg: computes the arithmetic mean across the group
  • $min / $max: returns the smallest or largest value seen
  • $push: builds an array containing every value in the group
  • $addToSet: builds an array of unique values only
  • $first / $last: grabs the value from the first or last document in each group as it arrives

Grouping by a Compound Key

db.sales.aggregate([
  { $group: {
      _id: { region: "$region", year: "$year" },
      totalRevenue: { $sum: "$amount" },
      avgOrderValue: { $avg: "$amount" },
      products: { $addToSet: "$product" }
  } },
  { $sort: { "_id.year": 1, totalRevenue: -1 } }
])

Using an object as _id lets you group by more than one field at once. The resulting documents carry a nested _id containing region and year, so downstream stages like $sort need to reference _id.year and _id.region using dot notation.

Grouping Everything Into One Document

db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: {
      _id: null,
      grandTotal: { $sum: "$amount" },
      averageOrder: { $avg: "$amount" },
      largestOrder: { $max: "$amount" }
  } }
])
Note: The document produced by $group only contains the _id field and whatever accumulator fields you define. Any other original fields are dropped unless you capture them with an accumulator like $first or $push.
Note: $group requires scanning every matching document to compute its accumulators, so it cannot use an index for its own logic. Always run $match beforehand to shrink the input as much as possible.