querydrill

Learn ›Advanced aggregation ›Array and conditional expressions

Filter an array, then calculate

arrays$add$eq$filter$multiply$reduce$set

Narrow the array first, then compute over what is left. Written as a nested expression it looks dense, so it is worth building up one layer at a time - the shape recurs constantly once you recognise it.

Suppose:

{
  _id: 1,

  items: [
    {
      product: "Laptop",
      category: "Electronics",
      price: 1000,
      quantity: 1
    },
    {
      product: "Book",
      category: "Books",
      price: 20,
      quantity: 2
    }
  ]
}

Question:

Calculate the total value of Electronics items in each order.

Think:

items[]

↓ keep only Electronics

$filter

↓

Calculate total

$reduce

Conceptually:

filter
→ reduce

Example:

{
  $set: {
    electronics: {
      $filter: {
        input: "$items",
        as: "item",

        cond: {
          $eq: [
            "$$item.category",
            "Electronics"
          ]
        }
      }
    }
  }
},
{
  $set: {
    electronicsTotal: {
      $reduce: {
        input: "$electronics",
        initialValue: 0,

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

This pattern is good to understand even if you wouldn’t always write it exactly this way.

Try it

Filter the array, then reduce what survived:

db.orders.aggregate([
  {
    $project: {
      electronicsTotal: {
        $reduce: {
          input: {
            $filter: {
              input: "$items",
              as: "item",
              cond: { $eq: ["$$item.category", "Electronics"] }
            }
          },
          initialValue: 0,
          in: {
            $add: [
              "$$value",
              { $multiply: ["$$this.price", "$$this.quantity"] }
            ]
          }
        }
      }
    }
  },
  { $limit: 5 }
])

Read it inside out and it is two steps, not one dense expression. Orders with no Electronics come back as 0, not missing - initialValue decided that.

Practise this

1 exercise