How do you remove a nested dictionary(map) from Firestore? - swift

I'm trying to make a method that removes a nested dictionary from my firebase database. The documentation says to use FieldValue.delete() but that only works for dictionaries on the first level. Setting the key equal to nil doesn't work either; this changes its value to "null" in firebase, but doesn't delete it.
database
.collection("users")
.document(userID)
.updateData([
"dict1":[
"dict2":***thisIsTheValueINeedToDelete***
]
])

FieldValue.delete() works for nested maps as well. You will need to use the dot notation to call out the full path of the nested map.
.updateData(["dict1.dict2": FieldValue.delete()])

Related

Flutter Firestore Query List Using WhereIn

I have a Firestore collection I am querying and referencing another document that I have already queried prior (teamRecord). In this case everything is working up to the final 'user_inchallenge' call. In this case, the user_inchallenge is a DocumentReference and the teamRecord.usersInTeam is a List of DocumentReferences. However, there whereIn call does not seem to be working properly. I only want to return docs where user_inchallenge is held within teamRecord.usersInTeam.
FirebaseFirestore
.instance
.collection('activity_units')
.orderBy('activity_value', descending: true)
.where('challenge_reference', isEqualTo: challenges.reference)
//below is not working
.where('user_inchallenge', whereIn: teamRecord.usersInTeam.asList())
As a note, if I remove the .asList() call in usersInTeam I get the error:
The argument type 'BuiltList<DocumentReference>' can't be assigned to the parameter type 'List'.
I also tried isEqualTo, arrayContains, but none of them have returns the filtered results.
The whereIn parameter expects a List, and it seems that BuiltList is not actually a List.
You'll want to convert your BuiltList to a List (e.g. by calling asList() or toList() on it) and then pass that to the Firestore call.
I filed issue #10407 on the GitHub repo to see if the types can be change to be Iterable.

How do I add a new Map entry to a Map without overwriting the entire Map?

In my Firestore database, I have a document with a Map called 'Videos'.
In it, I add another Map into it that includes my video data.
So it looks like this:
Videos (Map) :
2022-Oct-03-14:24 (Map):
Title: Hi
Now what I want to do is, add another Map to the Videos with a different date.
How do I do this in Flutter?
Currently, I am doing:
db.collection('Collection').doc('Document').update({
'Videos': {
'2022-Oct-03-14:55': {
'Title': 'Ok'
}
}
});
But what this does is, it overrides the entire Videos map and assigns this new map I give, so the old Map with the Title Hi gets deleted. How do I insert new maps?
While Alex's answer is correct if you had an array field, you only have map fields. In that case, you can use . notation to updates/write only the nested field:
db.collection('Collection').doc('Document').update({
'Videos.2022-Oct-03-14:55.Title': 'Ok'
});
Also see the Firebase documentation on updating a field in a nested object.
For adding a new object inside the Videos map, you have two options available. The first one would be to use the . (dot) notation, as #FrankvanPuffelen mentioned in his answer, which will work perfectly fine. This means that it will indeed create the Videos field if it doesn't exist, and it will add the 2022-Oct-03-14:55 subfield inside it if that doesn't exist, and then the Title you put in there. However, since it's an update operation, this solution will work only if the document already exists, otherwise, it will fail.
If you need to create the Videos field inside a document that doesn't already exist, then you should consider using the set() function with merge true. However, this solution doesn't work using the . notation. So for that, I recommend you check my answer in the following post:
Problem with the initial creation of a map structure within a document in firestore
The second solution would be to read the document/map first. It's not mandatory but it will help you check the existence of an existing object. Why, because those objects are maps, and when it comes to maps, if you add a record that has the same key, then it replaces the old value with the new one. So I recommend you have to check that first.
So in order to perform the addition operation, you have to:
Read to read the document.
Get the Videos map in memory.
Check if a video already exists.
Add the new map inside the Videos.
Write the document back to Firestore.
If you'll have a map with the exact same key as a previous key, then the data will be overwritten, meaning that you'll don't see any change in the key, but only in the value if it's different.

Delete specific value from firebase database using swift

Firebase Database
I tried using this bit of code but it doesn't seem to work. I take the name the user selects and store it in nameList.
Lets say I store Blake Wodruff in nameList[0].
How do I remove only that name?
var nameList = [String](repeating: "", count:100)
func remove() {
print(nameList[countAddNames])
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child(nameList[countAddNames]);
usernameRef.removeValue();
}
To write to or delete a node, you must specify its entire path. So to delete node 0 from your JSON, you'd do:
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child("0");
usernameRef.removeValue();
Or a bit shorter:
let usernameRef = Database.database().reference().child("Candidate 1/alton/0");
usernameRef.removeValue();
If you only know the name of the user you want to remove, you'll need to first look up its index/full path before you can remove it. If you have the data in your application already, you can do it in that code. Otherwise you may have to use a database query (specifically .queryOrderedByValue and .queryEqualToValue) to determine where the value exists in the database.
Also see: Delete a specific child node in Firebase swift
Once you remove a value from your JSON structure, Firebase may no longer recognize it as an array. For this reason it is highly recommended to not use arrays for the structure that you have. In fact, I'd model your data as a set, which in JSON would look like:
"alton": {
"Jake Jugg": true,
"Blake Wodruff": true,
"Alissa Sanchez": true
}
This would automatically:
Prevent duplicates, as each name can by definition only appear once.
Make removing a candidate by their name as easy as Database.database().reference().child("Candidate 1/alton/Jake Jugg").removeValue()
For more on this, also see my answer to Firebase query if child of child contains a value

Flutter Firestore getting incomplete array

I'm trying to fetch an array of strings I have saved on my database but I'm getting the array back with missing values, only the first value is shown inside of it. Here's the structure of my database:
Path: /users/uid/services <-- this is the array
Path: /services/uid <--service document
The code I'm using to retrieve the users is:
_getWorkers() async {
var query = await Firestore.instance.collection('users').where('services', arrayContains: widget.category['uid']).getDocuments();
query.documents.forEach((doc) {
workers.add(doc.data);
List<String> values = List.from(doc.data['services']);
print('services:' + values.toString());
});
var test = await Firestore.instance.collection('users').document('PtBD2EMSTodvlx6WHUx8QOHHLNA2').get();
print('actual services:' + test['services'].toString());
}
Both query and test get data back, but the services array only contains the first value.
Its difficult to answer without actually seeing the entire DB structure. But still on the outset i can see only one error with your code.
When you are trying to execute the where() call, try adding the snapshots to retrieve all relevant data.
Since await is also used, it is much better to call the listen() on it and then read the values to be added to the worker
Try using this below code in place of your first line.
await Firestore.instance.collection('users').where('services', arrayContains: widget.category['uid']).snapshots().listen((query)=>query.documents.forEach((doc)=>print(doc.data['services'])));
For some reason Firebase wasn't returning the updated array. Tested it again today and it worked with the same code. Sorry for bothering, thanks for the help anyways.

Update string with firebase swift

I am trying to update a string with firebase swift but I am getting an error that I do not know how to get rid of.
I have this code part that is getting an error:
self.dbRef.child("feed-items/\(dataPathen)/likesForPost").updateChildValues("likesForPost": "7")
The error I am getting is expected "," seperator just before the :. I am using dbRef in another code part so I know i works and the dataPathen is being printed just before the above code part, so that is working too.
Can anyone help me with this bug?
Just change
self.dbRef.child("feed-items/\(dataPathen)/likesForPost").updateChildValues("likesForPost": "7")
To
self.dbRef.child("feed-items/\(dataPathen)/likesForPost").updateChildValues(["likesForPost": "7"])
And if you are only looking for incrementing a particular value at a specific node you might wanna check my answer's :- https://stackoverflow.com/a/39465788/6297658, https://stackoverflow.com/a/39471374/6297658
PS Prefer runTransactionBlock: to update properties like likeForPosts as there might be a moment when two users try to like same post at the same moment (Highly Unlikely, but still a possibility...),using updateChildValues might end up just updating like only from one user. But runTransactionBlock: keep firing until the changes of that thread have been committed to the node
updateChildValues accepts [AnyHashable:Any] dictionary:
self.dbRef.child("feed-items/\(dataPathen)/likesForPost")
.updateChildValues(["likesForPost": "7"])
Whenever updating values at any reference in Firebase Database, you need to pass a dictionary parameter for updateChildValues method as [AnyHashable: Any] for your path reference. So just update your code of line as below:
self.dbRef.child("feed-items/(dataPathen)/likesForPost").updateChildValues("likesForPost": "7")
Also if you need to update more than 1 key-value pairs then you can pass those key-value pairs inside dictionary by seperating using comma as below:
self.dbRef.child("feed-items/(dataPathen)/likesForPost").updateChildValues(["likesForPost": "7", "otherKey": "OtherKeyValue"])