MongoDB: Update Operations for arrays
Update operations for field(s) is fairly straight forward in mongoDB, but when it comes to updating an array, it gets complicated at times. One of the examples would be: Updating the data of a child Object in an array field.
We’ll go through the different updates using examples.
Let’s start with the data that we will be working on:
skills
is an array object in every traveller object and we’ll be performing updates on those arrays.
Updating matched array elements
Take the case where we would want to update theskills
objects where the ‘activity’ is ‘Hiking’ by adding another field called isHiker
-
In order to update the element in the activity array instead of replacing the whole array, we do
db.travellers.updateMany({ 'skills.activity': 'Hiking' }, { $set: { 'skills.$.isHiker': true } })
The $
operator in skills.$.isHiker
does the magic. — skills.$
will automatically refer to the element matched in our filter.
Similarly, if you want to update an element of only one traveller, we need to build the query to get the traveller and activity that needs to be updated.
db.travellers.updateOne({ "_id": ObjectId("5d89e6617668172b55d73861") }, { 'skills.activity': 'Snow Boarding' }, { $inc: { 'skills.$.level': 1 } })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
This will update the snow boarding activity object’s level by (incrementing by) 1.
Updating multiple elements in the array
- updating all the elements in the array.
db.travellers.updateMany({}, {$inc: {'skills.$[].level': 10}})
{ "acknowledged" : true, "matchedCount" : 4, "modifiedCount" : 4 }
$[]
is the key here. It updates all the elements in the array by increasing the level of all the activities by 10.
- updating multiple elements that match the query.
db.travellers.updateOne({
name:'John'
},{
$set: {'skills.$[el].isActive': true}
},{
arrayFilters: [{'el.level': {$gt: 4}
}]
})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
The above statement adds a new field isActive
to the elements with activities that have their level more than 4.
$[el]
references to all the elements that pass the condition in the arrayFilters.
arrayFilters
is an array for all the different filters that are added.
For example:
db.travellers.updateOne({
name:'John'
},{
$set: {'skills.$[el].isActive': true},
$inc: {'skills.$[ol].level': 1}
},{
arrayFilters: [
{'el.level': {$gt: 4}},
{'ol.level': {$lte: 4}}
]
}
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
Adding/ Removing elements
- Adding
Add one element
db.travellers.updateOne({name: 'Dwight'}, {$push: {skills: {activity: 'swimming', level: 2}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
$push
adds an element to the existing array.
Add multiple elements
db.travellers.updateOne({name: 'Pam'}, {$push: {skills: {$each: [{activity: 'swimming', level:10},{activity: 'scuba diving', level:8}]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
$each
along with $push
allows you to add multiple elements to the array.
Along with $each
you have also the option to add $sort
and $slice
to $push
$sort
— sorts the array while updating the array.
$slice
— slices the array with the number specified.
db.travellers.updateOne(
{name: 'Pam'},
{$push:
{skills:
{
$each:
[
{activity: 'swimming', level:10},
{activity: 'scuba diving', level:8}
], $sort: {level: 1}, // -1 for decreasing order.
$slice: 1 }
}
}
)
This sorts the new array elements along with the existing elements in the array. $slice
removes the first element in the array after $sort
is done.
It always follows the order $each
, $sort
, $slice
whatever the order we use in the query.
Add using $addToSet
db.travellers.updateOne({name: 'Mary'}, {$addToSet: {skills: {activity:'Hiking', 'level': 5 }}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
$addToSet
— Doesn’t add duplicate values.
- Removing
Using $pull
db.travellers.updateOne({name: 'Mary'}, {$pull: {skills:{'activity': 'Skiing'}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
$pull
removes the matched element from the skills array. If there are multiple elements with the same ‘activity’, all of them will be removed.
Using $pop
db.travellers.updateOne({name: 'John'}, {$pop: {skills: 1}})
// {skills: -1} removes the first element
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
$pop
removes the first or the last element in the array. (1 — removes last element, -1 — removes the first element)