After learning about querying documents, it's time to learn how to update documents. If you know how to find your data, it will make it easy for you to update them as well. So without wasting time, let's get started !
Table of Content :
Update methods in mongo
Methods that helps us with updating documents in mongodb are:
- updateOne
- updateMany
In this post, we will work with trainers
collection. Enough of pokemons !! For the start, we will keep the structure simple:
{
name: "Ash",
age: 18,
pokemons: [
{ name: "pikachu", type: "electric", level: 16 },
{ name: "charizard", type: "fire", level: 30 },
{ name: "squirtle", type: "water", level: 12 }
],
badges: ["boulder badge", "cascade badge"],
exp: 200,
currentTown: "Pallet Town"
}
We will add data and change structure according to our need to make it easy while learning to update data.
Updating Documents
One important operator that you should know while updating documents is $set
. If we try to update documents without this operator, it will result in error or unexpected behavior.
Update methods take first parameter as your document filter, to filter the documents that you want to update and second parameter as your update that you want to apply to the filtered documents. There is a third parameter but we will learn about it later in the post.
Basics
# update the name of a trainer
> db.trainers.updateOne(
{name: "Ash"},
{$set: {name: "Misty"}}
)
# update age of all the trainers to 18
> db.trainers.updateMany({}, {$set: {age: 18}})
# add a new field to all the trainers
> db.trainers.updateMany(
{},
{$set: {hasPokemon: false}}
)
You don't know but mongo is very smart and if you try to update a field with the same value it already has, it ignores it (Like a pro)
Update Operators
Update operators help us with updating documents in various ways.
Operators:
- $inc : Increment or decrement numeric values
- $unset : Remove a field from document
- $rename : Rename a field
- $min : To update a numeric field with the given value if it is less than the existing value
$max : To update a numeric field with the given value if it is more than the existing value
$upsert : If the document exists, then update it, else create the document
$mul : To perform multiplication on numeric fields
There are other operators that help you work with arrays as well and we will discuss them later.
So, let's see some examples for the above operators and their usage
Trainers Document Structure
{
name: "Ash",
age: 18,
pokemons: [
{ name: "pikachu", type: "electric", level: 16 },
{ name: "charizard", type: "fire", level: 30 },
{ name: "squirtle", type: "water", level: 12 }
],
badges: ["boulder badge", "cascade badge"],
exp: 200,
currentTown: "Pallet Town",
bagItems: {
pokeballs: 8,
heals: 10
},
isFunny: true
}
Examples :
- Increment & decrement numeric values in document
# increment the experience of "Ash" by 50
> db.trainers.updateOne({name: "Ash"}, {$inc: {exp: 50}})
# decrease the number of pokeballs in Ash's bag by 2
> db.trainers.updateOne({name: "Ash"}, {$inc: {"bagItems.pokeballs": -2}})
- Updating numeric values according to current value with
$min
and$max
# update the number of heals in Ash's bag if current no. of heals are less than the update value
# this will only update if the passed value is less than the value in db
> db.trainers.updateOne({name: "Ash"}, {$min: {"bagItems.heals": 25}})
# update exp of brock to 300 only if the new exp. is greater than the current exp
> db.trainers.updateOne({name: "Brock"}, {$max: {exp: 300}})
- Performing multiplication on numeric field
# let's multiply Ash's exp (and make him invincible !!)
> db.trainers.updateOne({name: "Ash"}, {$mul: {exp: 100}})
- Let's see operations on fields and start with removing a field from doc
# remove "isFunny" field from all the docs
> db.trainers.updateMany({}, {$unset: {isFunny: ""}})
# NOTE 1: the value you pass to the field can be anything. Field name is the important value here
# NOTE 2: You can remove nested fields in docs as well e.g. "bagItems.heals"
- Renaming a field
# rename "exp" field to "experience"
> db.trainers.updateMany({}, {$rename: {exp: "experience"}})
- Updating a document and if doesn't exist, then create it.
> db.trainers.updateOne(
{name: "Paul"},
{$set: {age: 19, exp: 200}},
{upsert: true}
)
Here you see the third argument that update method accepts. It is like options that you can change for a query. By default upsert
is set to false
. By making upsert
true, we got the update or create behavior in our update.
Result of the above query
{
name: "Paul",
age: 19,
exp: 200
}
As you can see, mongo not only creates the document with the new fields but also adds the field which we passed as filter in first argument. Cool isn't it (I told you mongo is smart)
Updating Arrays
Updating arrays is a bit different than updating other fields in a document or nested documents. But it's not rocket science.
Let's start with some operators that can help us do basic operations with arrays.
-
$push
; add element to array in a doc -
$pull
: remove the specified element from array in a doc -
$pop
: remove element from array either from end or start -
$each
: helps to push multiple elements at once instead of just one -
$addToSet
: treats an array like a set and only add the value if it doesn't exists
Example Time :
# add a new pokemon to Ash's pokemons
> db.trainers.updateOne(
{name: "Ash"},
{$push: { pokemons: { name: "bulbasaur", type: "grass", level: 17 }}}
)
# add more than one pokemon to Ash's pokemons with $each
> db.trainers.updateOne(
{name: "Ash"},
{
$push: {
pokemons: { $each: [
{name: "butterfree", type: "bug", level: 20},
{name: "piggeotto", type: "flying", level: 14},
{name: "Krabby", type: "water", level: 13}
]}
}
}
)
# remove a badge from Ash's badge array (because we are evil)
> db.trainers.updateOne(
{name: "Ash"},
{$pull: {badges: "boulder badge"}}
)
# let's also take away a pokemon from Ash (for fun)
> db.trainers.updateOne(
{name: "Ash"},
{$pull: {pokemons: {name: "krabby"}}}
)
# pop a pokemon from misty from the end
> db.trainers.updateOne({name: "Misty"}, {$pop: {pokemons: 1}})
# pop a pokemon from misty from the start
> db.trainers.updateOne({name: "Misty"}, {$pop: {pokemons: -1}})
# we let's give a badge to Brock but we need to make sure he doesn't have two badges of same type. So we need to treat array like a set
> db.trainers.updateOne(
{name: "Brock"},
{$addToSet: {badges: "Thunder Badge"}}
)
# try making the above query again, and you will notice, it will not add another Thunder Badge to Brock's badges.
- Combine $addToSet with $each to add multiple unique values at once
- Pulling remove all matching elements from array, not just a single match.
More array operations
There are other operations that are related to positions in array. May be you want to update the fourth element in array (at 3rd index), or you want to update all the matched elements in an array and not just one. There are many situations like that.
Working with index
# Increment the level of first pokemon of Ash by 1
> db.trainers.updateOne({name: "Ash"}, {$inc: {"pokemons.0.level": 1}})
We can find a specific element in array and update that specific element without knowing its index.
$
: Instead of passing an index, we can use this positional operator to refer to the matched results from our filter. See the example below to understand.
Query and update the matched element in array
# add a health field to all the trainer's pikachu.
> db.trainers.updateOne(
{"pokemon.name": "pikachu"}, # query document
{$set: {"pokemons.$.health": 150}} # update to apply
)
# '$' operator will figure out which element of the query document matched and then apply update on them
NOTE:
$
operator only updates the first element and not all elements in array. What that means ? It means that if a trainer has more than one pikachu, only the first pikachu in his pokemons array will be updated.
Using array filters option
The above problem with positional operator that it only updates the first match in array can be solved easily by using array filters. It helps you choose all the documents that you want to update. Let's make a good situation for query and solve it.
Suppose, we want to find trainers that have experience of 200 or above and teach their grass type pokemons "Solar Beam" move. A trainer can have multiple grass type pokemons, so we have to teach all grass type pokemons "Solar Beam" and not just one.
Query :
> db.trainers.updateMany(
{experience: {$gte: 200}},
{$set: {"pokemons.$[poke].move": "Solar beam"}},
{applyFilters: [{"poke.type": "grass"}]}
)
Explanation :
- First is our document filter, to filter documents from collection
- Next is our update. As you can see, we used
$[poke]
syntax. Here,poke
is the identifier which will help us filter docs in array filters. You can name it anything. I named itpoke
, you can use something likeelem
,el
etc. - At last, we have our object in which we define our array filters.
- use
applyFilters
field to define filters for different identifiers you specified in$set
- You can only have one filter for each identifier.
// this won't work and not allowed
[{"poke.type": "grass"}, {"poke.level": {$gte: 14}}]
// instead use $and, $or and other operators
[{
$and: [
{"poke.type": "grass"},
{"poke.level": {$gte: 14}}
]
}]
Refer to the following in docs for more details
Note that in docs update
method is used and not updateOne
and updateMany
. Therefore, there when they want to use updateMany
, they pass { multi: true }
in options.
Finally chapter of update is closed ! If you have any questions, ask them in comments. I will try my best to answer them.
Next Post : Delete Documents
Prev Post : Query Documents - Part II
Top comments (0)