1

I'm trying to replace a map in Firestore with this code (Dart/Flutter):

    await FirebaseFirestore.instance.collection("test").doc("test").set({
      "abc": {"def": 123}
    }, SetOptions(merge: true));

    await FirebaseFirestore.instance.collection("test").doc("test").set({
      "abc": {"abc": 123}
    }, SetOptions(merge: true));

The problem is that the code doesn't replace the already existing map, but just adds te keys to it.

This is the output of the code in the Firestore console:

Output

Karel Debedts
  • 5,226
  • 9
  • 30
  • 70
  • 3
    That's not the behavior I've come to expect from Firestore; merge operations are shallow. Can you edit your question to show how you initialize `myMap`, print it right before calling the database, and show the updated code and its log output? – Frank van Puffelen Aug 15 '23 at 13:42
  • @FrankvanPuffelen I edited the question with an example – Karel Debedts Aug 15 '23 at 13:58
  • 1
    Hmmm... that definitely looks odd. I'm gonna check if I can reproduce that later today, unless someone gets to it before me. – Frank van Puffelen Aug 15 '23 at 14:03
  • 1
    It looks like this is the expected behavior with the `merge: true` option at the end. An [earlier SO question](https://stackoverflow.com/questions/46597327/difference-between-firestore-set-with-merge-true-and-update) explains the differences. – Nick Felker Aug 15 '23 at 15:10

1 Answers1

1

It keep forgetting this myself too, but the logic here is that:

  • Setting a document with merge options performs a deep merge of the provided data.

  • If you want to replace existing top-level fields, update the document instead of setting it.

A complete code sample:

doc.snapshots().listen((read) {
  print(read.data());
});

print("Delete");
await doc.delete();

print("Initial set");
await doc.set({
  "abc": {"def": 123}
}, SetOptions(merge: true));

print("Second set with merge");
await doc.set({
  "abc": {"abc": 123}
}, SetOptions(merge: true));

print("Update");
await doc.update({
  "abc": { "ghi": 123 }
});

This prints:

Delete
null

Initial set
{abc: {def: 123}}

Second set with merge
{abc: {abc: 123, def: 123}}

Update
{abc: {ghi: 123}}

For a running example of this, see https://zapp.run/edit/firestore-set-with-merge-2-z7fq06k28fr0

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks! Guess that answers it. So there's no option to have code that does the same as update, but also creates the document if it doesn't exist? – Karel Debedts Aug 15 '23 at 16:32
  • 2
    Unfortunately not. The only options if you need that would be to: 1) first try to read the doc, and decide what to do based on that - in a transaction, or 2) try an update, and handle the failure that throws when the doc doesn't exist yet. – Frank van Puffelen Aug 15 '23 at 18:37