MongoDB: Update Operations for arrays

Praneeth Paruchuri
3 min readSep 24, 2019

--

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)

--

--