Removing specific map/object from array in firebase - firebase

I'm experimenting with arrays and maps/objects in firestore. I wondered how can I remove a specific map from the array.
I tried something like this:
await Firestore.instance.collection('users').document(interestedInID).get().then((val){
return val.data['usersInterested'].removeWhere((item)=>
item['userID'] == userID
);
}).catchError((e){
print(e);
});
but I get this error in terminal:
Unsupported operation: Cannot remove from a fixed-length list
I don't really understand what it means. I did some googling and fixed-length list is exactly what it says. It's a list that has a fixed length and it can't be changed, but fixed-length list has to be declared explicitly. Growable list on the other hand doesn't need to be declared.
I haven't declared the fixed-length list in my firestore, yet it keeps saying that I can't remove elements from it. I can add / push elements however and remove them using:
'key': FieldValue.arrayRemove([value])
but I can't figure out how to remove the element based on a specific condition. In this case an userID.
Any suggestions?
Thanks a lot!

Figured it out.
await Firestore.instance.collection('users').document(interestedInID).updateData({
'usersInterested': FieldValue.arrayRemove([{}.remove(userID)])
});
I'm not sure, but I think get() simply allows you to read the document, but doesn't allow to make any changes to it.
Anyways, now it works

This may be a workaround:
You fetch your specific Array with key: values from Firebase
Put that in a temporary reference List in dart
Remove your specific Map from the reference List, like you did before
Update data in Firebase like so:
// make a reference List
List<Map> _modifiedUsersInterested = List();
// ... (fetch your Array first from Firebase, put those items in the reference List)
// remove the right Map from the reference List
_modifiedUsersInterested.removeWhere((item) => {
item['userID'] == userID
});
// set the reference List as your array in Firebase
await Firestore.instance
.collection('users')
.document(interestedInId)
.updateData(
{'usersInterested': _modifiedUsersInterested}
);

Related

Firestore : Update only a key-val pair

We have a firestore collection, where each document has only one field names.
names contains a map.
Now if, I just want to update a single key value pair in that map, is there a way, other than:
await FirebaseFirestore.instance
.collection(namesCollectionName)
.doc(docId)
.update(savedNames.toJson())
.whenComplete(() => {developer.log("Update saved names success")})
.catchError((error) {
developer.log("Update saved names failed: $error");
throw Exception("Update saved names failed: $error");
});
This code updates the entire map.
I am not sure if there is a way to update just a key value pair. I felt it would be more efficient!
Firestore processes each entries in the map you pass as a set operation on that field.
If you want to update nested fields use . notation to mark that field. So say you store the full name for each user keyed on the Stack Overflow ID as a map under the names field, that'd be:
users
.doc('ABC123')
.update({'names.13076945': 'Nithin Sai'})
If your names field is an array and you want to add a certain name if it doesn't exist in there yet, that'd be an array-union, which looks like this:
users
.doc('ABC123')
.update({'names': FieldValue.arrayUnion(['Nithin Sai'])});

How to query an array of objects in a Firebase Cloud Function, to get a matching object and then update

I am using a scheduled task in a Firebase Cloud Function to query an array which contains a number of objects that need to be updated if a matching condition exists. My current attempt is using the 'array-contains' method to get the objects, then loop over them to find a matching condition which will then batch update the items. This is my data structure:
I need to find an object that is <= the current time, and also if the 'active' value = false.
export const liveMeetingsTrigger = functions.runWith( { memory: '1GB' }).pubsub
.schedule('every 1 minutes').onRun(async context => {
const now = admin.firestore.Timestamp.now();
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
const batch = admin.firestore().batch();
liveMeetings.forEach(doc => {
if(doc.data().liveMeetingDate <= now && doc.data().active == false){
batch.update(doc.ref,'active',true);
}
});
return await batch.commit();
});
I have also tried using an exact object in the query instead of just using 'liveMeetingDate', but still get no results back, any help would be great - thanks.
Debugging: As the array I am trying to reach is inside of the (map) object 'liveMeetings' i have tried the dot notation (liveMeetings.meeting) with no success. Also trying a new collection with the the 'meeting' array at top level has provided no success.
Simple logging in the console (liveMeetings.size) shows that nothing is being returned on the query, so therefore the logging does not even reach the loop in the code.
As explained in this anwser the following query will not work:
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'liveMeetingDate').get();
because the meetings array contain some objects, instead of "simple" or primitive data (e.g. string, number...).
You could query it with the exact objects, like:
const obj = {active: false, liveMeetingDate: ..., meetingId: ..., ....};
const liveMeetings = await admin.firestore().collection('fl_content').where('meeting', 'array-contains', 'obj').get();
Another approach would be to create a new collection which contains the similar documents (same Document ID) but with a meeting Array that contains only the liveMeetingDate property.
Finally, note that since your Array is within a map, you need to do
await admin.firestore().collection('fl_content').where('liveMeetings.meeting', 'array-contains', ...).get();
(PS: I don't mark this question as duplicate since you expressly ask for more help in the comments of the duplicate question/answer)

How can I update nested List in Map that's all inserted to a List? Eg. List<Map<String, dynamic>>

I am trying to update or add to the end of the Lists that are in a Map and all inserted into a List that contains those maps. The List name is 'classes', what I have tried doing was using dot notation, so classes.index.example, but that doesn't work. Why...let's say I have two indexes in the list, If I go ahead and update index 0, the 'questions' and 'answers' will get inserted into that correct index, But for some reason, it will delete index 1 and any other that was created. It's as if It's overwriting all the data, but I don't understand why, I am not using 'setData()' Also, if I leave 'title' out, that too will get deleted??
Future updattingUserData(int index, List<dynamic> question, List<dynamic> answer, String title) async {
return await _collref.document(uid).updateData({
"classes.$index.questions": FieldValue.arrayUnion(question),
"classes.$index.answers": FieldValue.arrayUnion(answer),
//"classes.$index.title": title
});
}
Firestore doesn't have the capability of changing an array element knowing only its index within an array field. What you will have to do is read the document, modify the classes array in memory, then update the entire classes array field back to the document.

Flutter: How to remove a specific array data in firebase

I'm having a problem right now in firebase. Where I try to delete/remove a specific array data. What is the best way to do it? Ps. I'm just new in firebase/flutter.
My database structure:
Data that i'm trying to remove in my database structure(Highlighted one):
First create a blank list and add element in the list which you want to remove then Update using below method
Note : For this method you need the documennt id of element you want to delete
var val=[]; //blank list for add elements which you want to delete
val.add('$addDeletedElements');
Firestore.instance.collection("INTERESTED").document('documentID').updateData({
"Interested Request":FieldValue.arrayRemove(val) })
Update:
Much has changed in the API, although the concept is the same.
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('document_id')
.update(
{
'your_field': FieldValue.arrayRemove(elementsToDelete),
}
);
Firestore does not provide a direct way to delete an array item by index. What you will have to do in this case is read the document, modify the array in memory in the client, then update the new contents of the field back to the document. You can do this in a transaction if you want to make the update atomic.
This will help you to add and remove specific array data in could_firestore.
getPickUpEquipment(EquipmentEntity equipment) async{
final equipmentCollection = fireStore.collection("equipments").doc(equipment.equipmentId);
final docSnap=await equipmentCollection.get();
List queue=docSnap.get('queue');
if (queue.contains(equipment.uid)==true){
equipmentCollection.update({
"queue":FieldValue.arrayRemove([equipment.uid])
});
}else{
equipmentCollection.update({
"queue":FieldValue.arrayUnion([equipment.uid])
});
}
}
Example

Exclude document from query

I've been struggling with this problem for a while now, can't really find any direct answers anywhere to get around it. I really appreciate any help and more practical code to understand this.
I'm querying documents from a collection in Firestore. Then I'm putting them inside a custom List:
QuerySnapshot snapshot = await cardRef.limit(10).getDocuments();
List<CustomCard> cards =
snapshot.documents.map((doc) => CustomCard.fromDocument(doc)).toList();
Then I display the list like so:
//if cards != null
return Stack(children: cards);
However, I need to block documents from being added to 'cards' IF they contain a key inside a map of 'keys'.
I've tried to do something like this unsuccessfully:
if(snapshot.documents.contains('keys.$currentUserid'))
//dont add to list
But someone said on my previous post here, that I should hide the card on the client side. Is that the best approach since that card would still be added to the list? How would I do that?
in sum: 1) get all documents from a collection, 2) check each for specific key, 3) if they don't have key, add to list.
You're using snapshot.documents, which is a List<DocumentSnapshot> object. So what you're looking for is a way to derive another list from snapshot.documents with some of the documents removed.
My initial search for that flutter filter list, which shows that List.where is what you should be using for that. A simple example on this site: Flutter: Filter list as per some condition
So that'd lead to something like:
QuerySnapshot snapshot = await cardRef.limit(10).getDocuments();
List<DocumentSnapshot> filtered = snapshot.documents.where((doc) => ... /* your filter goes here */ )
List<CustomCard> cards = filtered.map((doc) => CustomCard.fromDocument(doc)).toList();

Resources