Skip to content

Aggregation Fundamentals

$group

Combine documents by a key. Totals, counts, averages — the stage interviews always probe.

IntermediateAbout 8 minutes

$group is SQL GROUP BY. _id is the key. Every other field is an accumulator. Fields you do not accumulate are gone. There is no SELECT * after a group.

db.orders.aggregate([
  { $match: { status: { $ne: "cancelled" } } },
  {
    $group: {
      _id: "$userId",
      total: { $sum: "$amount" },
      orders: { $sum: 1 }
    }
  }
])
Totals per customer. $sum: 1 counts documents.
{ _id: ObjectId("64a1b2c3d4e5f6a7b8c90001"), total: 18400, orders: 4 }
AccumulatorUse
$sumTotals; $sum: 1 to count
$avgAverages
$max / $minExtremes
$push / $addToSetCollect values into an array
$first / $lastOne value from the group — sort first if order matters
db.orders.aggregate([
  { $group: { _id: null, revenue: { $sum: "$amount" } } }
])

db.users.aggregate([
  { $group: { _id: { city: "$city", role: "$role" }, n: { $sum: 1 } } }
])
_id: null — one bucket for the whole collection. Compound _id — two dimensions.

$group does not sort. Add $sort. _id: "$userId" is a field path — the $ is required. { _id: "userId" } would put the string userId on every group (one bucket). $first without a prior $sort is 'some document in the group', not 'earliest order'.

Interview question

How does $group work, and what does _id mean inside it?

Think about it first.

Practice

$group, usually with a $match in front.