Update Arrays in a Document¶
If you need to modify an array embedded within a document, you can use an array update operator in your update method call. In this guide, we explain and show examples on usage of these operators including:
- First array element match positional operator:
$
- All array element match operator:
$[]
- Filtered positional operator:
$[<identifier>]
See the MongoDB server guide on Update Operators for a complete list.
Examples¶
The following examples use a database called test
and collection
called pizza
which contains documents that describe customers and
their pizza shop orders as array elements in field called items
. Use
the following sample document to follow the example queries:
[{ name: "Steve Lobsters", address: "731 Yexington Avenue", items: [ { type: "beverage", name: "Water", size: "17oz" }, { type: "pizza", size: "large", toppings: ["pepperoni"], }, { type: "pizza", size: "medium", toppings: ["mushrooms", "sausage", "green peppers"], comment: "Extra green peppers please!", }, { type: "pizza", size: "large", toppings: ["pineapple, ham"], comment: "red pepper flakes on top", }, { type: "calzone", fillings: ["canadian bacon", "sausage", "onion"], }, { type: "beverage", name: "Diet Pepsi", size: "16oz", }, ], }, { name: "Popeye", address: "1 Sweethaven", items: [ { type: "pizza", size: "large", toppings: ["garlic, spinach"], }, { type: "calzone", toppings: ["ham"], }, ], }]
Match the First Array Element¶
To perform the update on only the first array element of each document
that matches your query document in your update operation, use the $
positional array update operator. This update operator references the array
matched by the query filter and cannot be used to reference an array nested
within that array. For cases in which you need to access the nested arrays,
use the filtered positional operator.
The following code snippet shows how you can use the $
array update
operator to update the size of the first pizza order item to "extra large"
for the customer named "Steve Lobsters".
const query = { name: "Steve Lobsters", "items.type": "pizza" }; const updateDocument = { $set: { "items.$.size": "extra large" } }; const result = await pizza.updateOne(query, updateDocument);
Once the update operation is run, the document contains the new value for size for the first item:
{ name: "Steve Lobsters", ... items: [ { type: "pizza", size: "extra large", ... }
The query filter matches all documents that contain an element embedded in
the items
array that contain a value of pizza
in the type
field.
The updateDocument
specifies the update operation should set the
first array element match in items
to "extra large".
Note that we included both name
and items.type
fields in the query
filter in order to match the array in which we apply the $
operator. If we
omit the items.type
field from the query and specify the $
operator
in our update, we encounter the following error:
The positional operator did not find the match needed from the query.
Do not use the $
operator in an upsert
call because the $
is treated as a field name in the insert document.
Match All Array Elements¶
To perform the update on all of the array elements of each document that
matches your query document in your update operation, use the all
positional operator, $[]
.
The following code snippet shows how you can use the $[]
array update
operator to add "fresh mozzarella" to the toppings of all of Popeye's
order items.
const query = { "name": "Popeye" }; const updateDocument = { $push: { "items.$[].toppings": "fresh mozzarella" } }; const result = await pizza.updateOne(query, updateDocument);
After you run the update method, your customer document for "Popeye" should resemble the following:
{ "name":"Popeye", ... "items": [ { "type": "pizza", ... "toppings": ["garlic", "spinach", "fresh mozzarella"], }, { "type": "calzone", ... "toppings":["ham", "fresh mozzarella"], }, ] }
Filtered Positional Operator¶
In the previous sections, we used the $
operator to match the first
array element and the $[]
operator to match all array elements. In this
section, we use the filtered positional operator to match all embedded
array elements that match our specified criteria.
The filtered positional operator, denoted by $[<identifier>]
,
specifies the matching array elements in the update document. This
operator is paired with query filters in an arrayFilters
object in
your update operation's options
parameter to identify which array
elements to match.
The <identifier>
term is a placeholder value you designate that represents
an element of the array field name that prefixes it. For example, to add a
"garlic" topping to certain order items using this operator, format your
update document as follows:
{ $push: { items.$[orderItem].toppings: "garlic" } }
The <identifier> placeholder name must start with lowercase and contain only alphanumeric characters.
This update document specifies the following:
$push
: the update operatoritems
: the array in the document to updateorderItem
: the identifier for the filtered positional operatortoppings
: the field on theitems
array element to updategarlic
: the value to push onto thetoppings
array
Next, add the matching criteria in your arrayFilters
object in your update
operation's options
parameter. This object is an array of query filters
that specify which array elements to include in the update. To add the
"garlic" topping to order items of type "pizza" and "large size", pass the
following arrayFilters
:
arrayFilters: [ { orderItem.type: "pizza" }, { orderItem.size: "large" } ]
The following snippet shows the complete update method for this example:
const query = { name: "Steve Lobsters" }; const updateDocument = { $push: { "items.$[orderItem].toppings": "garlic" } }; const options = { arrayFilters: [{ "orderItem.type": "pizza", "orderItem.size": "large", }] }; const result = await pizza.updateMany(query, updateDocument, options);
After we run the method above, all of the large pizza order items for
customer "Steve Lobsters" now contain "garlic" in the toppings
field:
{ name: "Steve Lobsters", ... items: [ { type: "pizza", size: "large", toppings: ["pepperoni", "garlic"] }, { type: "pizza", size: "large", toppings: ["pineapple", "ham", "garlic"] ... }
Let's run through another example. Suppose "Steve Lobsters" wants to adjust their order to add "salami" as a topping to only the large pepperoni pizza, you can use the filtered positional operator to perform the update as follows:
const query = { name: "Steve Lobsters" }; const updateDocument = { $push: { "items.$[item].toppings": "salami" }, }; const options = { arrayFilters: [ { "item.type": "pizza", "item.toppings": "pepperoni", }, ], }; const result = await pizza.updateOne(query, updateDocument, options);
After we run the update method, the document resembles the following:
{ name: "Steve Lobsters", address: "731 Yexington Avenue", items: [ { type: "pizza", size: "large", toppings: ["pepperoni", "salami"], }, { type: "pizza", size: "medium", toppings: ["mushrooms", "sausage", "green peppers"], comment: "Extra green peppers please!", }, { type: "pizza", size: "large", toppings: ["pineapple, ham"], comment: "red pepper flakes on top", }, { type: "calzone", fillings: ["canadian bacon", "sausage", "onion"], }, { type: "beverage", name: "Diet Pepsi", size: "16oz", }, ], }