Arrays & Nested Documents
Positional Operators
$, $[] and $[<identifier>] for updating the element you actually matched.
IntermediateAbout 6 minutes
You matched an item. Now change that item. Three tools: `$` (the first match of the query), `$[]` (every element), `$[<id>]` with arrayFilters (every element that matches a filter, not necessarily the query).
db.orders.updateOne(
{ "items.name": "USB-C Cable" },
{ $inc: { "items.$.quantity": 1 } }
){ items: [
{ name: "Mechanical Keyboard", quantity: 1, unitPrice: 4500 },
{ name: "USB-C Cable", quantity: 3, unitPrice: 200 }
] }$ is only the first match, and the query must have matched that array. It cannot see arrayFilters. Two cables in items and you only bump the first.
// 10% off every line
db.orders.updateOne(
{ _id: ObjectId("64a1b2c3d4e5f6a7b8c90011") },
{ $mul: { "items.$[].unitPrice": 0.9 } }
)
db.orders.updateOne(
{ _id: ObjectId("64a1b2c3d4e5f6a7b8c90011") },
{ $inc: { "items.$[line].quantity": 1 } },
{ arrayFilters: [{ "line.name": "USB-C Cable" }] }
)- `$` — one element, the first that the query matched. Simple, easy to get wrong with duplicates.
- `$[]` — every element. 'Mark all items shipped' inside one order.
- `$[line]` + `arrayFilters` — every element that matches
line.…. This is what you want for 'all cables' or 'unitPrice > 1000'.
Interview question
How do you update one object inside an array without replacing the array?
Think about it first.
Match it, then use a positional operator. $ updates the first element the query matched. arrayFilters with $[identifier] updates every element that matches the filter. I do not rely on items.0.