querydrill

Learn ›Advanced aggregation ›Array and conditional expressions

$map, and how it differs from $unwind

arrays$map$multiply$project$unwind

$map applies an expression to every element of an array and gives you a new array back. It is the operator that lets you compute per-element values without $unwind scattering your document across many.

Suppose:

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

You want:

[
  {
    product: "Laptop",
    total: 2000
  },
  {
    product: "Mouse",
    total: 150
  }
]

Use $map.

{
  $project: {
    items: {
      $map: {
        input: "$items",
        as: "item",

        in: {
          product: "$$item.product",

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

Mental model:

Array

[A, B, C]

$map

A → transformed A
B → transformed B
C → transformed C

Basically:

array.map(...)

from JavaScript.

$map vs $unwind

This distinction is important.

Suppose:

items[]

Use $map

When you want to:

Keep the array
but transform its elements.

Example:

items[]

↓ calculate totals

itemsWithTotal[]

Use $unwind

When you want:

One pipeline document per array element.

Example:

Order
  ├── Laptop
  └── Mouse

↓

Order + Laptop
Order + Mouse

↓

$group by product

Remember:

Keep array structure
→ $map / $filter

Break array into documents
→ $unwind

Try it

db.orders.aggregate([
  {
    $project: {
      _id: 0,
      items: {
        $map: {
          input: "$items",
          as: "item",
          in: {
            product: "$$item.product",
            total: {
              $multiply: ["$$item.price", "$$item.quantity"]
            }
          }
        }
      }
    }
  },
  { $limit: 3 }
])

Three documents in, three out, each with a rebuilt array. $unwind would have given you one document per item instead.

Practise this

1 exercise