Flutter Firestore update if exist if not create

Update

on newer Firebase SDK the syntax changed; Firebase is FirebaseFirestore and setData is deprecated in favor of set + SetOptions.

FirebaseFirestore.instance.
  collection('tags').
  document(tag).
  set(data, SetOptions(merge: true))

@Doug is right, there is an option in the Flutter Firestore API:

Firestore.instance.
  collection('tags').
  document(tag here).
  setData(data, merge: true)

From the doc:

setData(Map<String, dynamic> data, {bool merge: false}) → Future<void>

Writes to the document referred to by this DocumentReference. If the document does not yet exist, it will be created.

If merge is true, the provided data will be merged into an existing document instead of overwriting.

Compare that with the doc from updateData, which explicitly fails.

updateData(Map<String, dynamic> data) → Future<void>

Updates fields in the document referred to by this DocumentReference.

If no document exists yet, the update will fail.


I don't know how it works with Flutter specifically (I haven't written a line of Dart in my life), but the other client APIs allow a second argument to set() (or in your case setData()) that allows a document to be either created if not existing, or merged in the case that it already does.

See the documentation for examples in other languages. It might just be a matter of passing { merge: true } as the second argument, like the web example, or possibly use of SetOptions.merge() like Java.


It appears that as of 2020-03-31, yes, you have to check whether or not the document exists first. setData(data, merge: true) doesn't actually obey the merge parameter. Here is the relevant GitHub issue

The behavior I'm getting is equivalent to a regular setData(), so it silently overwrites any existing data. An expensive workaround:

final Firestore _db = Firestore.instance;
final collection = 'my_collection';
final documentId = 'my_document';
final snapShot = await _db.collection(collection).document(documentId).get();
if(snapShot.exists){
  _db.collection(collection).document(documentId).updateData(myMap);
} else {
  _db.collection(collection).document(documentId).setData(myMap);
}

Update 2021:

Use set on the document and provide a SetOptions with merge: true. From the docs:

Sets data on the document, overwriting any existing data. If the document does not yet exist, it will be created.

If SetOptions are provided, the data will be merged into an existing document instead of overwriting.

var collection = FirebaseFirestore.instance.collection('collection');
collection
    .doc('doc_id')
    .set(yourData, SetOptions(merge: true));