71

I want to update a doc like this:

db.collection('users').doc(user_id).update({foo:'bar'})

However, if the doc user_id does not exists, the above code will throw an error. Hence, how to tell Firestore to create the student if not exists, in other word, behave like this:

db.collection('users').doc(user_id).set({foo:'bar'})
TSR
  • 17,242
  • 27
  • 93
  • 197
  • 1
    What is wrong with the set method? – J. Doe Sep 11 '18 at 13:28
  • I want Firestore to create the document automatically if the update method fails. Because Firestore cannot update a non existing document, it does not work – TSR Sep 11 '18 at 13:31
  • 1
    The answer here is correct, and is discussed in the documentation: https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document – Doug Stevenson Sep 11 '18 at 15:40
  • For anyone stumbling across this with the slightly different requirement of *rejecting* a `set` if the document already exists, see my answer [here](https://stackoverflow.com/a/68707546/2172566) – forresthopkinsa Aug 09 '21 at 06:16

2 Answers2

145

I think you want to use the following code:

db.collection('users').doc(user_id).set({foo:'bar'}, {merge: true})

This will set the document with the provided data and will leave other document fields intact. It is best when you're not sure whether the document exists. Simply pass the option to merge the new data with any existing document to avoid overwriting entire documents.

For more detailed about managing data with firestore check this link

J. Doe
  • 12,159
  • 9
  • 60
  • 114
  • 3
    note that it's not the same as using update - if you delete a field in your model (using js "delete" keyword ) and use "set+merge" the field will stay there in the database, while using update will remove the field in the database as intended. Possible approach might be not to use js delete, but instead set the field to null. – vir us Dec 15 '19 at 11:00
  • 9
    In my case I need to increment a counter with update("field_name", admin.firestore.Fieldvalue.increment(1)). The merge option cannot work for this scenario. How do I achieve this? – Urchboy Feb 09 '20 at 11:02
  • @AndyFusniak you can find the Go equivalent right here: https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document , in the second example click the "More" dropdown to find Go. – Nikita Fuchs Jun 25 '20 at 09:15
  • Nice trick. Do you know if it still works with v9 of the SDK? Particularly the merge flag – sayandcode Jul 21 '22 at 12:01
13

If you need things like created and updated timestamps, you can use this technique:

let id = "abc123";
let email = "john.doe@gmail.com";
let name = "John Doe";
let document = await firebase.firestore().collection("users").doc(id).get();
if (document && document.exists) {
  await document.ref.update({
    updated: new Date().toISOString()
  });
}
else {
  await document.ref.set({
    id: id,
    name: name,
    email: email,
    created: new Date().toISOString(),
    updated: new Date().toISOString()
  }, { merge: true });
}

This will create the document if it doesn't exist with created and updated timestamps, but only change the updated timestamp if it exists.

Dale Zak
  • 1,106
  • 13
  • 22
  • 2
    You might also want to look into Firestore Transactions: https://firebase.google.com/docs/firestore/manage-data/transactions#transactions – JCraine May 05 '21 at 03:26
  • 3
    and NEVER user client timestamps with firestore, always use `FieldValue.serverTimestamp()` – Michael B. Feb 05 '22 at 14:49
  • 1
    I don't agree about the timestamp. Firebase timestamps are objects with added methods. If you transfer data to other systems, for example a relational database, you'll lose those methods, potentially breaking stuff. We ALWAYS use UTC timestamps for this reason. – Spock Aug 01 '22 at 11:26
  • 1
    whats the reason for putting {merge:true} when you already know that document doesn't exist in the previous check? – Karol Be Nov 10 '22 at 08:42
  • @KarolBe you're correct, there is no point. – codingexplorer Jun 26 '23 at 16:19