Cloud Firestore triggers " Your trigger must always point to a document" - firebase

what i am trying to do is ... my document flow
batch(collection) -> {batchName}(document) -> subjects -> {subjectName} -> attendance
i want to trigger firestore function whenever there is an update in the attendance object
So in the documentation of the Cloud Firestore triggers, i found this line "Your trigger must always point to a document" with an example ... here is the link to the documentation
which gave me hope that it is possible to do that and i am failing to achieve this,
exports.attendenceTrigger = functions.firestore.document('batche/{batchName}/subjects/{subjectName}/attendance')
is it possible to do it ? if yes then what am i doing wrong?

You have specified a path to a collection
'batche/{batchName}/subjects/{subjectName}/attendance'
(col) (doc) (col) (doc) (col)
However, your path must point towards a document so valid paths include:
// Triggers a function when a doc in subjects sub-collection is changed
'batche/{batchName}/subjects/{subjectName}'
// or
// Triggers a function when a doc in attendance sub-collection is changed
'batche/{batchName}/subjects/{subjectName}/attendance/{attendanceId}'
Is attendance a sub-collection? If yes, then use the second path above to listen trigger a function when a document in that collection is created/modified/deleted. If attendance is a field in a {subjectName} document then you should use the first line and access the attendance field from the snapshot snapshot.data().attendance.

Related

Firebase snapshot listener filter

I have a Firestore DB. Is it possible to filter snapshots coming from it based on some field if add add a listener? What I need is: “send me an updated document only if this field equals this value”
What I surely can do is just check manually each new snapshot and return/propagate document if it passes the filter but I was thinking about sparing some transferred data and hit less limits
You can make a query by filtering something then adding a listener for this like the below code:
// get all document in collection "cities" that has attribute "state" equal to "CA"
db.collection("cities").whereField("state", isEqualTo: "CA")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
let cities = documents.map { $0["name"]! }
print("Current cities in CA: \(cities)")
}
Ref: https://firebase.google.com/docs/firestore/query-data/listen#listen_to_multiple_documents_in_a_collection
Cloud Firestore listeners fire on the document level. So if you have multiple fields in a document and the value of the fields in the document changes, your listener will fire. So you'll have to pay a read operation, each time something in the document changes.
It's true that you can attach a listener and get only the documents that have set a field to a particular value, but that doesn't mean you can restrict the SDK to read the value only when that field is changed to a value of your choice.
There is no way you can read a document, only when a field gets a particular value. You're always charged with a read operation, each time a value changes, no matter what the value is. So if the new value is the value that passes your filters, then you can go ahead with your logic.
I was thinking about sparing some transferred data and hitting less limits.
Everything in Firestore it's about the number of reads, writes, and deletes you perform. And the amount of bandwidth you are using. But unfortunately, you cannot reduce the costs that way.

DELETE operation javascript fetch() with REST API firebase

I use the following API request in order to delete all texts (so called cps) of one section (one section contains many cps)
await fetch(`https://12345-default-rtdb.europe-
west1.firebasedatabase.app/cps/${userId}.json?section_id=${mysection}`,{method:'DELETE',
// });
userId is correct, mysections is the current sectionId, section_id is the key of the sectionId in the JSON document. (eg: -N09gWdyQlV7OsPpEx7t or -N09g_HjbcFCQFBiIX0A see below) In this example all cps of all sections are being deleted. So the conditional query does not work.
What is going wrong here? Thanks!
The tree within firestore looks like this:
cps -> user1 -> -N09gWdyQlV7OsPpEx7t
cps -> user1 -> -N09g_HjbcFCQFBiIX0A
....

Flutter cloud firestore : query document->map->array

My db design is above picture. I wanna create a query which returns user where tags are matched. But i didnt any solution to query.
This is my flutter code:
But it doesnt work. How can i query array of map of document?
The courses is an array and not a map so you cannot use the dot notation to query. If the courses is made a collection (or a sub-collection) on it's own then you would be able to query users easily:
users -> {userId}
(col) (doc)
courses -> {courseId}
(col) (doc)
You would have to include a field userId in each course document which would be used to identify which user owns that course.
await firestore.collection("courses").where("tags", arrayContainsAny: tagKeys)
This will return all courses where the tags array contains at least 1 item in the tagKeys list. If you need exact match i.e. all the tags in tagKeys must be present in Firestore document then you would have to restructure the database as mentioned in this answer.
Fetching all matching documents might not be ideal since you just need user IDs that matches the tags. In that case you can store a field which contains tags from all the courses in a single array field in the user document.

Firebase Firestore Documents changes history (like Activity log/ History for changes in each Doc)

I'm trying to make an Activity log system or history for my docs, so every time a field is modified in a document i want to record or save that so i can see after changes history made on each document.
how i can achieve that ? i don't want to save the full doc on each change and then have tons of duplicated docs, if possible i just want to get the changed field (ex. name: 'john' -> name: 'jack').
i don't want to save the full doc on each change and then have tons of duplicated docs
Once a document has changed it becomes a new document. So you won't have duplicate documents unless you make changes that were previously made. Please also note that in Cloud Firestore there are no field-level permissions or access to a document. It's the entire document, or nothing. So if you want to change a field within a document for example from:
userName = "John"
into
userName = "Jack"
You'll will get the entire document and not only the userName property that has been changed.
Cloud Firestore listeners fire on the document level. There is no way to get triggered with just particular fields in a document.
If you want to get notified only of specific fields, consider adding an extra collection with documents that only contain those fields. This sort of data duplication is quite common in NoSQL solutions such as Firestore and for that, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase real-time database but same principles apply to Cloud Firestore.
For a database schema you can also take a look at my answer from this post.
The best way to achieve something like this is to store the before and after changes happening to the doc, in a new document, which you can add in a subcollection. The changes are available with cloud functions onUpdate trigger. I have written in depth about this topic on my blog, have a look.
https://blog.emad.in/audit-logs-for-firestore-documents/
You can obtain this by creating a cloud function that triggers on all document updates in all collections:
--trigger-resource=projects/$PROJECT_ID/databases/(default)/documents/{collection_id}/{document_id}
In the cloud function you can obtain all the updated fields and their values through the data object.
Python example:
def main(data, context):
# Extract resource
resource = context.resource
resource_split = resource.split('/')
collection_name = resource_split[-2]
document_id = resource_split[-1]
# Get old fields
data_old_values = data['oldValue']
data_old_values_fields = data_old_values['fields']
# Get updated fields
data_updated_mask = data['updateMask']
data_updated_fields = data_updated_mask['fieldPaths']
# Get new field values
data_new_values = data['value']
data_new_values_fields = data_new_values['fields']
# `data_updated_fields` is a list of the fields that has been changed
# `data_old_values_fields` is a dictionary with the old values of the document
# `data_new_values_fields` is a dictionary with the new values of the document

How to get field ref data in firebase in single call?

I have below firestore collections.
-Converstions(collection)
(document) {participants: {userid1: true, userid2: true}, messages: [subcollection]}
-Users(collection)
(document)(userid1){userName: 'Test1', ...}
(document)(userid2){userName: 'Test2', ...}
Now I need to query for conversations a users is in, I can do this with
firebase.firestore().collection('conversations')
.where(`participants.${uid}`, '==', true);
What this does is gets all conversation a users is participating in, I need to now get the user details from id for each document in those conversation. If we make another call to UserRef to get the user details it will make extra request for each conversation data. I wanted to know if there is easy way to get user details in single call to the firebase.
When a user is added to a document, you could also add some display information about that user (either from the app or Cloud Functions).
There is no way to return data referenced elsewhere. You either need to duplicate or make multiple fetches.

Resources