263

In Cloud Firestore there are three write operations:

  1. add()
  2. set()
  3. update()

In the docs it says that using set(object, { merge: true }) will merge the given object with the existing document.

The same happens when you use update(object)... so what is the difference? It seems strange that google would duplicate functionality like this.

Tanner Stern
  • 128
  • 1
  • 8
ZuzEL
  • 12,768
  • 8
  • 47
  • 68

5 Answers5

588

The way I understood the difference:

  • set without merge will overwrite a document or create it if it doesn't exist yet

  • set with merge 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
})
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Scarygami
  • 15,009
  • 2
  • 35
  • 24
  • 6
    but where have you found `create` method in the API? – ZuzEL Oct 06 '17 at 07:49
  • 2
    https://cloud.google.com/nodejs/docs/reference/firestore/0.8.x/DocumentReference#create for node.js. It seems the web API doesn't have that method. Wasn't sure which platform you are on :) – Scarygami Oct 06 '17 at 07:59
  • 34
    Another distinction you can mention is that `set` operates on document-shaped data, where `update` takes field path and value pairs. This means you can make changes to deeply nested values with `update` that are more cumbersome with `set`. For example: `set({a: {b: {c: true}}}, {merge: true})` vs `update('a.b.c', true)`. – Gil Gilbert Oct 06 '17 at 16:10
  • If I want to update a value in a document, It makes sense that I want to update documents that already exists, so I think set + mergeall is not that usefull because it will creat it the document does not exists – John Balvin Arias Aug 11 '18 at 21:12
  • If data you provide to the set command has a field which is null, will it set the field to null if it is already present in the database or will it leave it alone? – GGizmos Mar 18 '20 at 23:11
  • why they make different behavior for `set` and `update`, this is so confusing, and they even didn't clarify this on the doc. – JIE WANG Apr 11 '20 at 16:25
  • 7
    set with merge option will overwrite the field no matter what. However update will be ignored if this is not the last update. For Example if you trigger the update action on an offline device, and get back online 3 days later. – Julie Rankata Oct 16 '20 at 09:16
  • Will update throw an error if the document does not exist? – Kennedy Nyaga Dec 01 '20 at 20:29
  • Does this work for subcollections? I am doing the following: ` await admin.firestore().collection('households').doc(hhid).collection('currentMembers').doc(uid).set({ joinedDate: admin.firestore.FieldValue.serverTimestamp(), }, { merge: true, }); ` – MrHarvey Jan 03 '21 at 15:51
  • Now we just need a better way of updating arrays of objects. The current SDK heavily encurages objects, since they are clearly the easiest to update without fear of overwriting anything. – DarkNeuron Dec 21 '21 at 08:46
  • Beware that adding `{merge:true}` as an option for `update` (as opposed to `set`) appears to ignore the constraint that the document must exist! – D W Jul 21 '22 at 20:01
  • How about when the field does not exist for update operation? What will happen? – Bitwise DEVS Aug 17 '22 at 00:34
  • Note that if you need to use a dynamic variable in updateDoc() you have to wrap the entire thing in brackets and then in back ticks. E.g update({ [\`a.${b}.c\`]: true }) In this case "a" and "c" are static fields in the database and "b" is dynamically created key that is passed in during the call. Got it from this thread: https://stackoverflow.com/a/72247881/11058429 – Jeremy Bailey Dec 16 '22 at 17:55
  • Re-initialise DocumentReference if you want to add new item otherwise it will keep on updating last added documen. Re-initalisation creates new documentid – Arman Reyaz Jan 22 '23 at 14:45
133

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
   }
 }`
Frederiko Ribeiro
  • 1,844
  • 1
  • 18
  • 30
Finlay Percy
  • 6,763
  • 4
  • 22
  • 28
  • 1
    Have you tried testing this yourself? There is a section in the documentation: "To update some fields of a document without overwriting the entire document, use the update() method..."[link](https://firebase.google.com/docs/firestore/manage-data/add-data#update-data) – Finlay Percy Jun 06 '18 at 10:03
  • yes I have tested this. It was a few weeks ago, but I am positive I came out with the result I wrote. I searched up why it didn't work as expected etc. I will test it out now. – ravo10 Jun 06 '18 at 10:16
  • 2
    I figured it out. I only tried this with an array before. Where I wanted to add an object to the array, and everything got overwritten for that array. It does not work with fields containing an array... it does stand it docs. – ravo10 Jun 06 '18 at 11:24
  • 2
    Just got to the same conclusion after tests. I hope they will add an option that has the same effect as `{ merge: true }` to the update function. – Johnride Mar 13 '19 at 00:47
  • Helped me a lot, I was having troubles updating a map inside firestore and **set** with **merge: true** worked like a charm. Thanks. – Jonathan Arias Jul 02 '19 at 16:36
  • 8
    To avoid overwriting data in nested fields (as in the answer above) when using `update`, you can use [dot notation](https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects). The overwrite behavior of `update` is different if you do/don't use dot notation. – ultraGentle Jan 15 '20 at 17:05
24

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
})
CodeManDan
  • 281
  • 2
  • 3
7

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 Maps because both set and update will overwrite arrays unless you explicitly use the array-specific operators such as arrayUnion.

geg
  • 4,399
  • 4
  • 34
  • 35
0

Scarygami answer holds true for firebase version 9 as well. Small changes should be considered though:

  • use setDoc instead of set. For example, instead of using db.collection('users').doc('random-id').set({ 'admin': true });, use setDoc(doc(db, 'users', 'random-id'), { "admin": true })
  • use updateDoc instead of update. For example, instead of using db.collection('users').doc( random-id').update({"age": 27}), use await updateDoc(doc(db, 'users', 'random-id'), { "age": 27})
abdou_dev
  • 47
  • 5