What is the recommended way to track document changes in firestore? - firebase

Let's say I have a employees collection where I have one document per employee and I want to keep record of all changes that were made to a single employee doc. I was thinking of the following approach:-
Have a pendingEmployeeWrites collection where client is
only allowed to create documents. Each doc here will have an
employeeId field (this id is generated on client side for new employees).
Cloud function will be invoked whenever such a doc is created and then it validates the data. If valid, the employeeId doc in employees collection is overwritten with this data. Otherwise the pendingEmployeeWrites doc is updated to set isFailed as true. Client app is only allowed to read from employees collection.
Keeping pendingEmployeeWrites as a flat collection instead of a sub-collection allows me to pull all changes made by a user as well as all changes for a particular document. Does this approach make sense or is there a better approach that I should consider?

Related

Firebase Firestore order by 2 fields conditionally

I have some documents in Firestore with two Timestamp fields named lastUpdated and lastProcessed in addition to other fields. lastUpdated field is updated when a user updates the record's fields via web console. lastProcessed field is updated when the backend function processes the document (as a result of user clicking a button).
Following are the possible combinations of these 2 fields.
User has only updated the document, but yet to process (lastUpdated == some_timestamp, lastProcessed == '')
User has updated the document, and then processed (lastUpdated < lastProcessed)
User has updated the document, processed and re-updated (lastUpdated > lastProcessed)
My requirement is to execute a query to get a subset of these records (say top 10), ordered by its most recent timestamp. So when evaluating a record for the ordering, lastUpdated field should be considered for scenarios 1 and 3 above. But lastProcessed field should be considered for scenario 2.
Is this possible with Firestore?
When querying the Firestore database it is not possible to execute the logic you explain in your question (i.e. calculate on the fly the scenario to be applied and define which field shall be used in the query).
One classical solution is to add an extra field to the document which contains the value to be queried for. The value of this field can be calculated (according to the business logic) when you modify the document from your frontend, or via a Cloud Function triggered in the backend each time the doc is changed.
The main advantage of using a Cloud Function is to prevent users modifying the value of this field.

How to write to a document and read the id of it within a single transaction in Firestore?

I am doing the user authentication where I have this case:
Read from vendor_type document and if it returns null(doesn't exist) then continue the transaction,
Create new user using .auth().createUserWithEmailAndPassword(email,password),
Read the new users ID,
Write to vendor_type document some of the new user's detail such as name, surname, userId -->> userId is the problem, how can I create a user and get the ID within a single transaction, can I even do that? ,
Take the newly created ID of the user, and create a new vendor document with that ID.
So far I don't have any code to post because I don't know if this is even gonna work so I didn't start. If you have any idea how to implement this, please let me know. The main issue is getting the user ID while still in the transaction.
At the time of writing, it is not possible to combine in one transaction the creation of a user through the createUserWithEmailAndPassword() method from the Auth service AND a write to the Firestore service.
They are two different services offered by Firestore and therefore you cannot combined calls to these two different services in one transaction.

Saving users scores and favorites in Firestore Database

I am working in a small project that uses Firestore database as a backend. I explain about the database so it is understood what I need:
Basically I have a collection that contains a list of documents where each one of them represent a game. For each game I have the name, cover image, info, category, etc.
I also have a collection of the users, where I have the specific UID for each user (retrieved from the auth section), email, etc.
What I want now is to save the score that some user may have in some of these games, as well as the favorite games that the user could save. What I don't get to understand is how to create the connection between the users and the games. For example, I thought that I should save the users score creating a collection within each document(game) in the first collection that mentioned. But when I create this collection with ID "scores" it asks me for the first document where I have to facilitate an ID (if not automatic) and then I don't know how to proceed.
I have read also that I would have to create additional collections in the root folder like "favorites" or "scores" specifying the UID of the user but, how do I connect the user UID, the score, and game which the user got that score from?
I hope I explained myself properly. Thanks.
Firstly, I agree with Doug's comment above. The Firestore tutorial videos are a great resource!
In terms of connecting data to your user, you have some options. You can either:
Create sub-collections under each user. Such as /users/{user_id}/favorites. Favorites could be a sub-collection or an array of game_ids depending on your use case.
Store a userID field in the documents in a top level "scores" or "favorites" collection. Then you can query for scores in the /scores collection by adding a where userID == {user_id} clause to your query of the /scores collection.

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

Correlating Firestore indexes to slugs in a webapp

I'd like my web app router slugs to correspond to my Firestore documents data.
For example:
www.mysite.com/restaurants/burger-king
/restaurants <- Firestore Collection
/restaurants/mcdonalds <- Firestore Document
/restaurants/burger-king <- Firestore Document
This is easy enough, as I can assign the name as a slug-friendly UID in Firestore. The difficulty arises with CRUD functionality. I need to be able to rename my item titles, but Firestore does not permit you to rename indexes, which is the issue I'm facing.
One SO solution I saw was to delete the old record and creates a new one at the updated index. That's problematic for me, because sub-collections would be hard to transfer from the client side.
Are there more elegant solutions?
You don't have to identify a document by its ID. If you're able to ensure uniqueness of a document field value, you could instead query a collection for an ID value in a known field, then use the results of that query to satisfy your REST API. Then, you can change the value of that document field as often as you want, in order to satisfy required changes to the public API.

Resources