Difference between set with {merge: true} and update
DatabaseFirebaseGoogle Cloud-FirestoreDatabase Problem Overview
In Cloud Firestore there are three write operations:
-
add
-
set
-
update
In the docs it says that using set(object, {merge: true})
will merge object with existing one.
The same happens when you use update(object)
So what is the difference if any? It seems strange that google will duplicate logic.
Database Solutions
Solution 1 - Database
The way I understood the difference:
-
set
withoutmerge
will overwrite a document or create it if it doesn't exist yet -
set
withmerge
will update fields in the document or create it if it doesn't exists -
update
will update fields but will fail if the document doesn't exist -
create
will create the document but fail if the document already exists
There's also a difference in the kind of data you provide to set
and update
.
For set
you always have to provide document-shaped data:
set(
{a: {b: {c: true}}},
{merge: true}
)
With update
you can also use field paths for updating nested values:
update({
'a.b.c': true
})
Solution 2 - Database
Another difference (extending Scarygami's answer) between "set with merge" and "update", is when working with a nested values.
if you have a document structured like this:
{
"friends": {
"friend-uid-1": true,
"friend-uid-2": true,
}
}
and want to add {"friend-uid-3" : true}
using this:
db.collection('users').doc('random-id').set({ "friends": { "friend-uid-3": true } },{merge:true})
will result in this data:
{
"friends": {
"friend-uid-1": true,
"friend-uid-2": true,
"friend-uid-3": true
}
}
however update
using this:
db.collection('users').doc('random-id').update({ "friends": { "friend-uid-3": true } })
will result in this data:
`{
"friends": {
"friend-uid-3": true
}
}`
Solution 3 - Database
Per docs: https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects
Dot notation allows you to update a single nested field without overwriting other nested field. If you update a nested field without dot notation, you will overwrite the entire map field.
As stated above, this replaces entire friends structure.
db.collection('users').doc('random-id').update({
"friends": {
"friend-uid-3": true
}
})
This does not.
db.collection('users').doc('random-id').update({
"friends.friend-uid-3": true
})
Solution 4 - Database
Further adding on to the answers above, if you want to delete nested fields in a map then you may want to use update
or set
depending on your use case.
If you start with the following and want to remove all profile entries other than "user1"
then you have two options.
{
"users": {
"profiles": {
"user1": ...,
"user2": ...
}
}
Update
This will overwrite profiles
with whatever is provided
update({
'users.profiles': { 'user1': ... }
})
Set
This will merge the deletes into the existing profiles, leaving whatever wasn't deleted
set({
users: {
profiles: {
'user2': FieldValue.delete(),
'user3': FieldValue.delete(),
...
}
}
}, { merge: true })
This only applies to Map
s because both set
and update
will overwrite arrays unless you explicitly use the array-specific operators such as arrayUnion
.
Solution 5 - Database
One more interesting behaviour which can be useful but not obvious.
When you make batch
update, and don't want to check if all documents you trying to update exist.
With batch update
your request will fail if at least one document does not exist.
With batch set {merge: true}
your request will successfully update all existing documents and create dummy documents for non existent ids.
Possible use case: Merging google analytics into your documents from analytics reporting api
when this api provides data for existing and deleted documents together.