querydrill

Learn ›Aggregation ›Arrays in a pipeline

Why $group cannot see inside arrays

Everything so far grouped across documents. The moment the numbers you need are inside an array - order line items, for instance - $group cannot see them, and the pipeline you would expect to work quietly returns the wrong totals.

Back to the orders collection.

{
  _id: 1,

  items: [
    {
      product: "Laptop",
      price: 1000,
      quantity: 1
    },
    {
      product: "Mouse",
      price: 50,
      quantity: 2
    }
  ]
}

Suppose the question is:

Find the total quantity sold for each product.

Your first instinct might be:

{
  $group: {
    _id: "$items.product",
    totalQuantity: {
      $sum: "$items.quantity"
    }
  }
}

But items is an array.

You don’t yet have:

One document = one product

You have:

One document = one order

We need to change the shape.

This is where $unwind comes in.

Try it

Run the instinct first, so you have seen it fail:

db.orders.aggregate([
  {
    $group: {
      _id: "$items.product",
      totalQuantity: { $sum: "$items.quantity" }
    }
  },
  { $limit: 3 }
])

No error. _id came back as a whole array of product names, and $sum over an array of arrays gave 0. That is the failure mode worth remembering: it does not crash, it just answers a different question.

With $unwind in front:

db.orders.aggregate([
  { $unwind: "$items" },
  {
    $group: {
      _id: "$items.product",
      totalQuantity: { $sum: "$items.quantity" }
    }
  },
  { $sort: { totalQuantity: -1 } },
  { $limit: 5 }
])