querydrill

Learn ›Advanced aggregation ›Array and conditional expressions

When you do not need $unwind

arrays$add$multiply$reduce$set$unwind

$unwind is not free, and it is not always necessary. If the answer you want stays within a single document, the expression operators can compute it without ever splitting that document apart - and knowing when to choose which is a genuine interview discriminator.

Question:

Calculate the total value of every order.

You have:

{
  items: [
    { price: 100, quantity: 2 },
    { price: 50, quantity: 1 }
  ]
}

You want to keep:

One result per order.

Using $unwind would turn one order into multiple documents, then you’d need to group again.

Instead:

One document
   ↓
items[]
   ↓
calculate one total

→ $reduce
{
  $set: {
    orderTotal: {
      $reduce: {
        input: "$items",
        initialValue: 0,

        in: {
          $add: [
            "$$value",

            {
              $multiply: [
                "$$this.price",
                "$$this.quantity"
              ]
            }
          ]
        }
      }
    }
  }
}

Decision:

Need to keep one document per order?

→ $map / $filter / $reduce

Need each item to participate as an independent document,
especially for grouping?

→ $unwind

Try it

One total per order, and the order never came apart:

db.orders.aggregate([
  {
    $set: {
      orderTotal: {
        $reduce: {
          input: "$items",
          initialValue: 0,
          in: {
            $add: [
              "$$value",
              { $multiply: ["$$this.price", "$$this.quantity"] }
            ]
          }
        }
      }
    }
  },
  { $project: { orderTotal: 1 } },
  { $limit: 5 }
])

Five documents in, five out. The $unwind version needs a $group afterwards just to put back together what it took apart.