7

I'm using flutter web and firebase for a project and got stuck on a problem. I'm trying to update a map in an array in firestore.

using this:

var val = [];
    val.add({'groupUID': groupUID, 'inviteStatus': status});
    var userInviteUID;
    await users
        .document(uid)
        .get()
        .then((value) => userInviteUID = value.data['inviteUID']);
    await invites
        .document(userInviteUID)
        .updateData({'invites': FieldValue.arrayUnion(val)});

I got this result: firestore structure

What I want to do is just change the 1 to a 2 in the map. I thought that it would update since its the same value but it just adds it to the array.

I looked around on stack and saw some ways to do it like copying the entire array and changing it where I need to, then adding it back.

But I wanted to know if there was a way to avoid that by adding some modifications to my code. Also let me know if there's a better structure I should use. Appreciate the help!

UPDATE:

var ref = invites.document(userData.inviteUID);
    ref.get().then((value) async {
      var invitesList = value.data['invites'];

      switch (status) {
        case 1:
          break;
        case 2:
          var index;
          invitesList.asMap().forEach((key, value) {
            if (value['groupUID'] == groupUID) index = key;
          });
          invitesList.removeAt(index);
          await invites
              .document(userData.inviteUID)
              .updateData({'invites': FieldValue.arrayUnion(invitesList)});
          break;
        default:
      }

So I looked at some print statements and seen that the elements with the matching group uid is removed, but looking at firebase, the array isn't overwritten anything...any ideas?

FINAL UPDATE:

var ref = invites.document(userData.inviteUID);
        ref.get().then((value) async {
          var invitesList = value.data['invites'];
    
          switch (status) {
            case 1:
              break;
            case 2:
              var index;
              invitesList.asMap().forEach((key, value) {
                if (value['groupUID'] == groupUID) index = key;
              });
              invitesList.removeAt(index);
              await invites
                  .document(userData.inviteUID)
                  .setData({'invites': FieldValue.arrayUnion(invitesList)});
              break;
            default:
          }

Fixed it by changing updateData to setData.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
BackAtItAgain
  • 85
  • 2
  • 7

2 Answers2

8

I looked around on stack and saw some ways to do it like copying the entire array and changing it where I need to, then adding it back.

That's exactly how you are supposed to modify the contents of arrays in Firestore documents. Firestore doesn't support updating array elements by index.

maykhid
  • 129
  • 1
  • 10
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • I had the hope that something would have changed over the last 3 months on Firestore documents, but as it seems, Doug Stevenson's reply is still the only answer. You need to copy the entire array, change it locally, and after that make the post request on Firebase. – Foudas Oct 20 '20 at 20:37
  • Is this answer still valid? I am curious about how to handle synchronisation for this situation. 2 Client can update the document at the same time. May it cause to one of them lose its data ? – veli Jul 25 '23 at 05:27
1

setData creates a new document if it already doesn't exist but if the document exists, the data will be overwritten. To prevent this from happening, you could use SetOptions(merge: true) if you wish to append the data:

set(someData, SetOptions(merge: true))

You can also use update method and provide it the updated data, the pseudo code could look like this:

List<Map<String, dynamic>> updatedList = [...];
Map<String, dynamic> updatedData = {
  'existing_map': updatedList,
};
  
var collection = FirebaseFirestore.instance.collection('collection');
collection 
    .doc('doc_id')
    .update(updatedData);
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440