2

I'm using Firestore in my application, and I have a map field called "votes" for user's upvotes or downvotes. It looks like this:

enter image description here

I want to add an option to delete an element from there, this is what I got now:

 //getting the user's votes dictionary and removing the post from it.
    userRef.getDocument { (doc, error) in
        if let _ = error { completion(false) }
        guard let dict = doc?.data()?[USER_VOTES] as? [String: Any] else { return }
        currentDict = dict
        currentDict.removeValue(forKey: id)
    }

    //setting the votes dictionary with the updated one.
    userRef.setData(currentDict) { (error) in
        if let _ = error { completion(false) }
        else { completion(true) }
    }

to me, It looks not really efficient, because each time a user is trying to remove an element from this dictionary, I have to write to the database. which can slow down the process and to my understanding, the free tier of Firestore limits the number of writes. Is there a better way, maybe deleting it right from the user's document? I tried to look for answers, but couldn't find anything that worked for me. This one for example: Removing a dictionary element in Firebase looks like what I need to do, but I couldn't get it to work.

EDIT: I tried deleting it like that

    userRef.updateData([
        USER_VOTES:[
            id: FieldValue.delete()
        ]
    ]) { (error) in
        if let _ = error { completion(false) }
    }

The app crashes says:

Terminating app due to uncaught exception 'FIRInvalidArgumentException', reason: 'FieldValue.delete() can only appear at the top level of your update data 
John Doah
  • 1,839
  • 7
  • 25
  • 46
  • 1
    Look into use of `FieldValue.delete()` to call out the individual map field to remove. You'll have to use the field value dot notation to identify the specific field (e.g. "votes.XXX"). https://firebase.google.com/docs/firestore/manage-data/delete-data#fields – Doug Stevenson Nov 26 '19 at 20:05
  • 1
    Btw, this data architecture won't scale well on Firestore because that single document will become too large as your userbase grows. Firestore is optimal when there are many small documents, not the other way around. This is NoSQL in general. – trndjc Nov 26 '19 at 20:10
  • @DougStevenson Thank you, Looks exactly like what I wanted! – John Doah Nov 27 '19 at 07:00
  • @bsod Thanks, I did not think about it. Is there a better way? Im trying to add upvote and downvote option to my app. – John Doah Nov 27 '19 at 07:01
  • @DougStevenson I tried this, but it didn't work, I could not get to the votes.id to delete it. I tried following the docs but it didn't work for me. – John Doah Nov 27 '19 at 18:51
  • Please edit the question to show what you tried that didn't work the way you expect. – Doug Stevenson Nov 27 '19 at 19:05
  • @DougStevenson I edited the question. I tried to get to the item I want to delete with it's id. but I get an error. – John Doah Nov 27 '19 at 19:37
  • It looks like you're not using the dot notation to find the field as I suggested in my first comment. – Doug Stevenson Nov 27 '19 at 20:00
  • @DougStevenson I tried but USER_VOTES is a string and I could not do it. – John Doah Nov 28 '19 at 09:36

1 Answers1

4

To be able to delete a specific field you should follow the steps mentioned here.

For your case I have created the following under collection 'voting': enter image description here

So to delete vote2 field you should use:

// Get the `FieldValue` object
let FieldValue = require('firebase-admin').firestore.FieldValue;

// Create a document reference
let fieldRef = db.collection('voting').doc('votes');

// Remove the 'vote2' field from the document 'votes'
let removeField = fieldRef.update({
  vote2: FieldValue.delete()
});

And here is the document after running the above: enter image description here

EDIT :

If the data model is a map inside a document, for example: enter image description here

Then here is how you can delete a field inside the array which is inside the document:

let docRef = db.collection('voting').doc('user1');

let removeField = docRef.set({'votes':
    {['id_vote_1'] : FieldValue.delete()}}, {merge: true});

Here is the document after running the above:

enter image description here

Waelmas
  • 1,894
  • 1
  • 9
  • 19
  • Thank you, I tried this, but my problem is I have a user document, inside this document I have the votes dictionary. and this solution is not working for me. – John Doah Dec 06 '19 at 19:04
  • @JohnDoah I have updated my answer showing also how you can delete a field, inside a map which is inside a document. – Waelmas Dec 07 '19 at 14:01
  • Sorry took me few days to respond. This is working now. Thank you very much! – John Doah Dec 09 '19 at 19:51