Aggregation Expressions
Array Expressions
$size, $filter, $map and $reduce — transforming arrays without leaving the document.
$unwind explodes an array into documents. Array expressions reshape the array in place. Use unwind when the next $group is across orders. Use $map / $filter / $reduce when the answer still belongs on this order.
db.orders.aggregate([
{
$set: {
bulky: {
$filter: {
input: "$items",
as: "line",
cond: { $gt: ["$$line.quantity", 1] }
}
},
items: {
$map: {
input: "$items",
as: "line",
in: {
name: "$$line.name",
quantity: "$$line.quantity",
unitPrice: "$$line.unitPrice",
lineTotal: { $multiply: ["$$line.quantity", "$$line.unitPrice"] }
}
}
}
}
}
])$$line is the variable from as. Omit as and the name is $$this. $ paths are still the parent document ($items). Mixing $line (a field named line on the order) with $$line is the usual bug.
{
$reduce: {
input: "$items",
initialValue: 0,
in: {
$add: [
"$$value",
{ $multiply: ["$$this.quantity", "$$this.unitPrice"] }
]
}
}
}| Operator | Use |
|---|---|
| $size | Length; [] is 0 |
| $filter | Keep elements that match cond |
| $map | One output element per input element |
| $reduce | Fold to one value; $$value is the accumulator, $$this the element |
| $arrayElemAt / $first / $last | One element by index or ends |
| $in | Expression form: { $in: ["MongoDB", "$skills"] } |
$size on a missing field errors — $ifNull: ["$items", []] first. Set operators ($setUnion, $setIntersection) exist for tag arrays; you do not need them until you are merging two lists. Units sold across the collection is still $unwind then $group (most-purchased-products). $map cannot see other orders.
Interview question
When do you use $map / $filter instead of $unwind?
When I am transforming the array on the same document — drop some lines, add lineTotal, fold a total. $unwind when I need to $group across array elements from many documents. Variables are $$this or $$name from as; parent fields stay $field.