How to stream subcollections from x amount of documents in Flutter - firebase

Hei. I have a Chats collection, that keeps track of chats and its users. Every chat has a messages subcollection.
I want to stream changes from the subcollection messages, but ONLY on the chats collection where you are a part of the users array.
Collectiongroup wont work as it streams for all subcollections...
The reason for this is to count all unread messages and show a badge in the app.

Related

How FireStore document reads in sub collections calculated

I have a root collection of products that contains 10 documents.
Each document has a subcollection of 100 documents and no other fields.
Now if I read the documents of the products collection by
db.collection("products").get();
how many reads will be charged, Since I'm getting documents of products collection having 10 documents I will be charged for 10 reads? or each document has a subcollection of 100 documents, so I will be charged for 10*100 =1000 document reads?
In Firebase Firestore you will be charged only for data you receive.
When reading a collection you won't get the subcollections with it.
That means if you have 10 produst with 100 items in theire subcollections. You will be charged only for those 10 products you get because you won't get the subcollections with that request.

Should I store chat messages in the chat document or in separate documents in Firestore using Flutter?

I'm making a real time chat app in Flutter and I've come to a point when I have to decide how I want to store messages in Firestore. I have 3 collections in Firestore, chats, users, and messages.
I'm currently storing every message as a separate document in the messages collection.
A message document looks something like this:
I'm storing the uid of the message sender and the id of the chat that the message was sent to, so later I can make query to Firestore like this:
// messages is the collection reference
Stream<QuerySnapshot> getChatMessages(String id) => messages
.orderBy('sentOn', descending: true)
.where('sentTo', isEqualTo: id) // gets all the messages that were sent to the given chat id
.snapshots();
However, this method isn't very good because of the number of reads or writes I have to do to work with my data in Firestore. Another big problem appears when I want to delete the chat and all of it's messages, as I would have to delete every message document separately and if I stored the messages in the chat document it would only be one delete call to Firestore. On the other side, earlier in another project when I stored all messages in a Map, I noticed that it would get quite slow at drawing the messages on the screen as their number increased.
What do you think? Which is the better option? Is there anything I missed or maybe another way to approach the problem? Please let me know.
Thanks. <3
There are several ways you can approach this. One of the ways to do it is:
Store all messages of a particular chat as a sub collection in the chat document.
So you'll have to delete your message collection.
For example:
In the chat collection, if we have a chat document with ID "123", the we can store all the messages of that chat document as a sub collection under the chat document.
This will reduce your number of reads and writes, thereby making your application more performant and fast.

Does a heavy document take much time in load from Firestore?

I am using both Firebase Database and Firestore in my app. I store users data like name, email, uid etc small details in documents of a collection as Users in firestore. It works perfectly. I made a node as Friends in firebase database to store friends list of a user. So whenever user open the app, it calls his information from Users from firestore and also his friends list from Friends from firebase database.
Now the thing is by this way it calls data from the Firestore and the Firebase database. So it means they are 2 requests/reads, one to Friends node and other to a document from Users collection. I think it would be better if i store friends list in Users document as an Array. So i will get only 1 read in Firestore. But i think that when the arrays of his friends list increases by 100+ elements. And also there are one or two more array lists like that. So will it take much time in retrieving a document from Users collection? or not? And which will be a better approach?
Here are the images of my current database structure as Users and Friends.
As per the Firestore usage and limits, the maximum size of a document is 1 MiB.
It means that as long as your user documents don't exceed the size limit, you can store friends data in arrays without a problem.
If you are planning to exceed the threshold, you may want to look for other options like creating subcollections to scale better as size of the subcollection doesn't affect the parent document's size in any way.
I built a chat app in flutter with firebase using mapping for each chat Text(only one doc was used in chat between 2 users). I observed that after I filled 1MB of data in doc, my mobile downloaded the chat history at 10-12 kbps from firebase.
Maybe the speed was a coincidence but I am sure that as your data grows in a single firestore doc, the mobile app does not bursty download the whole document simultaneously, instead it downloads at a much slower speed.
Please correct me If I am wrong.

Cloud Firestore Payments

I have a question regarding payment at the Cloud Firestore compared to the Realtime Database. At Firestore you pay per read/write per document, right? In other words: If I display a list of 1000 documents in a collection, do I pay for 1000 reads?
I have a few collections in my app with many (200-300) documents, which unfortunately all have to be displayed on one page. My app has about 10,000 active users. After the calculation I am definitely financially broke... :-)
Therefore my question: Are 300 elements also 300 reads taken into account if I save the 300 elements in ONE document as an Array and retrieve them? Is then only the one document calculated as a read? Or also the 300 elements from the created array?
If I display a list of 1000 documents in a collection, do I pay for 1000 reads?
You only pay for documents that are read on/from the server. Most Firestore SDKs implement a client-side cache, which may significantly reduce the number of documents that are read on/from the server.
I have a few collections in my app with many (200-300) documents, which unfortunately all have to be displayed on one page
One way to reduce the number of read operations is to model the data for that one page into a separate single document. This document is essentially the data for a single page in your app, meaning that you update it whenever any of the underlying data updates. That leads to more code when you write updates to the database, but it saves you 299 document reads for every user accessing the page.
Also see:
Cloud Firestore Pricing | Get to Know Cloud Firestore #3
Firestore: How are "reads" calculated for the quota?
Firebase firestore pricing for querying
Understanding Firestore Pricing

StreamBuilder Controls and Firestore Pricing

I have a two-part question. After reading through Firestore pricing, it says that you are charged based on the number of documents you read and write and the operations you perform such as deleting and some other factors. With that being said, I am using a StreamBuilder that continually refreshes itself whenever the list is scrolled whether there is new data in the database or not. Right now, the builder is fetching from a collection with very little data but that collect can grow to be bigger. With that being said, my questions are:
Each time the StreamBuilder refreshes itself to show new data, is it actually fetching all the documents again from the Firestore collection or is it only looking for changes and updating them? If it is fetching the documents again from Firestore, does Firestore consider this as downloading multiple documents each time it refreshes or does it count it only once and if there are updates to any new document fetched, those are counted separately?
If it fetches all the documents over and over again every 2 seconds or even less as in the current behavior, is there a way to limit this say to every 30 seconds or to when every a RefreshIndicator is used so as to avoid multiple unnecessary reads? I tried using a StreamController but the stream still refreshes every time the list is touched or every second.
Well i guess it depends a bit on your code. I think there are methods to listen to firestore changes constantly.
However if you use the most common queries then this should not be the case. Here my reasoning why, according to my understanding:
Streambuilder: The builder function is triggered everytime data hits the sink of a stream.
Sink is the input channel for any data. Streams immediately return data which is put in the sink.
Firestore: If you execute a firestore "query" it will read document by document and return it once it is read. Once all documents are read the connection will be closed.
If you now assign the firestore query as stream to your builder, example below. The builder is triggered when a document is read. In the builder you then probably build a widget which is displayed.
Once the firestore query has read all documents no new data will be pushed into the sink and therefore the builder will not be triggered anymore. This query will then be completed and no longer listen to changes as the connection will be closed.
Therefore the documents are usually only read once during the lifetime of a streambuilder.
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('your collection').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
//Your own code to handle the data
})
I recently build an app where I read tasks from firestore and process the documents via StreamBuilder. An easy way to test how often a document is read is by simply printing the document to your console in the builder section.
What I observed is that documents are only read once as long as the Widget tree in which the Streambuilder resides is not rebuild.
So to answer your question:
My understanding is that if the StreamBuilder refreshes or is initialized again then it triggers again the query and reads the data. According to the firestore documentation each read of the document is counting towards your limits and costs. So I would say yes it counts for all documents included in your query.
I am not sure how you constantly refresh or initialize the streambuilder, therefore I can't give you a clear answer. If you just use the code similar to above once during the build of the widget tree then it should be read only once...
Without some more details, I cannot provide more information.
Each time the StreamBuilder refreshes, it will query Firestore for the documents in the collection again. Firestore will count this as a read operation for each document retrieved. However, Firestore does not count it as multiple reads for the same document if it is fetched multiple times. Firestore only charges for each document read once.
Firestore provides real-time updates through the use of listeners. When you listen to a Firestore collection using a StreamBuilder, Firestore will automatically send updates whenever there are changes to the documents in the collection. This means that the StreamBuilder will only fetch the documents that have changed, rather than the entire collection. However, if the entire collection changes (e.g. if a new document is added), then Firestore will fetch the entire collection again.
Each time the StreamBuilder refreshes, it creates a new query to Firestore, which counts as a read operation. If you refresh the StreamBuilder frequently, this can lead to a large number of read operations. However, Firestore does not charge you for reading the same document multiple times in a short period of time. For example, if you refresh the StreamBuilder every second and the same document is retrieved each time, Firestore will only count this as one read operation for that document.
To limit the number of reads, you can consider using a caching mechanism to store the data on the client-side and avoid making unnecessary network requests. You can also adjust the frequency of fetching new data by using a timer or a debounce mechanism to avoid refreshing too frequently.

Resources