Welcome to the second post in my "MongoDB Animated š©" series, where I provide animated examples and explanations for MongoDB operations that I don't ever want to google again.
Introduction
In this post, we'll review different strategies for updating elements from embedded arrays.
I'll use the same example collection used in the previous article in this series. It consists of a simple collection of "donut combos" from a donut shop. Each combo has a name and an array of donuts that will be included if the customer chooses that combo. Here's the complete schema:
// donutCombo Schema
{
name: { type: String, required: true },
active: { type: Boolean, required: true },
donuts: [{
color: { type: String },
glazing: { type: Boolean }
}]
}
Updating elements
Updating all elements with $[]
Using the $set
operator combined with the all positional operator $[]
allows us to update all the array elements.
For example, let's say we want to take away the glazing from all donuts in all active documents:
db.donutCombos.updateMany({ active: true }, {
$set: {
'donuts.$[].glazing': false
}
});
This sort of operation can come in handy when you migrate to a new schema and your documents have embedded arrays. For example, if you needed to add a new property with a default value to all the array elements.
Another way to take away the glazing could be by removing the property glazing
from all donuts in all active documents.
This can be done using the $unset
operator in combination with the all positional operator $[]
:
db.donutCombos.updateMany({ active: true }, {
$unset: {
'donuts.$[].glazing': 1
}
});
Notice that, inside the
$unset
object, we must define the key (the property we want to remove) and the value, which doesn't impact the operation. In this case, we used "1", but you can also use an empty string, for example:'donuts.$[].glazing': ''
.
Updating the first element with $
You may need to update only the first element of your array. In that case, you should use the positional operator $
. This positional operator will allow us to apply changes to the first element that matches the query document
(the first parameter of the update
method). The array field must be a part of the query document
.
By combining the $
positional operator with $set
, we can update properties from the first array element that matches our query document
.
In the following example we will update the first white donut in every document and change it's color to "pink".
db.donutCombos.updateMany({ 'donuts.color': 'white' }, {
$set: {
'donuts.$.color': 'pink'
}
});
The $
positional operator can also be combined with $unset
to remove a property from the first array element that matches our query document
:
So to try that, let's remove the color from the first white donut found on each document.
db.donutCombos.updateMany({ 'donuts.color': 'white' }, {
$unset: {
'donuts.$.color': 1
}
});
Updating elements that match a filter with $[<identifier>]
To update a set of elements matching certain filters, we must use the filtered positional operator $[<identifier>]
where <identifier>
is a placeholder for a value that represents a single element of the array.
We must then use the third parameter (options) of the updateMany
method to specify a set of arrayFilters
. Here we will define the conditions each array element we want to update must meet to be updated.
In the next example, we will change every white donut color to green, only in the active documents.
db.donutCombos.updateMany({ active: true }, {
$set: {
"donuts.$[donut].color": "green"
}
},
{
arrayFilters: [{ "donut.color": "white" }]
});
Combining the filtered positional operator $[<identifier>]
(or in our example $[donut]
) with the operator $unset
, we can remove a property from all elements in the array that match our arrayFilter
criteria.
For example, we could remove the color of every white donut in every active document:
db.donutCombos.updateMany({ active: true }, {
$unset: {
"donuts.$[donut].color": 1
}
},
{
arrayFilters: [{ "donut.color": "white" }]
});
Try it yourself
I created a repo to try MongoDB queries in memory using Node.js with Jest and MongoDB Node driver. I used tests to execute the query and verify that everything was correctly updated. I also included a logger that prints the updated documents in the console displaying the changes that were applied using diff highlight syntax:
You can find the examples I included in this article in the tests
folder:
pawap90 / try-mongodb-queries
A simple sample project to try MongoDB queries in memory using Jest
try-mongodb-queries
A simple project to try MongoDB queries in memory using Jest. Includes a logger that logs the difference between the original test data and the data after the update using diff syntax highlight:
Dependencies
What you need to run this project:
- Node.js
(MongoDB is not required because it'll run in memory, handled by the package mongodb-memory-server-core
).
Try it out
1. Install dependencies
npm install
2. Run tests
npm test
Tools
Main tools used in this project:
Resources
For more info about updating arrays, here are a few resources from MongoDB's official docs:
Was this post useful? š¬
I'm interested in your feedback. Was this post useful? Would you like me to cover any other operation so you don't ever have to google it again?
Top comments (2)
For anyone who is reading this article in intention to find out how to deal with fields of array objects. This article is a bit outdated.
At least for db.version() - 7.0.6, mongosh version() - 2.3.2.
If you need to use replace operator
$
then you must always use arrayFilters with it .I don't know why mongo db dev team overcomplecates query syntax.
if you need to use conditions in order to fill fields then you have to create variable for each condition. And conditions must not be crossed.
Example for y'all to give tips :
Good topic and representation, nice animation.