Do Observables re-query ALL data when Firestore fields update? - firebase

Let's say I do a query against a Firestore collection over a date range or something. If I get an observable to the set of documents and iterate through it to build up a local collection, will it re-read all the data from Firestore every time there is a change in Firestore? Say this observable is from a where clause that contains 500 documents and I iterate through doing something:
this.firestoreObservable$.subscribe(documents => {
documents.forEach(async doc => {
// do something
})
})
If one field on one documents change on Firestore, will that count as another 500 document reads? If so (ouch!) what would the recommenced best practice be to keep from spending so many reads?
Thanks.

No. If only one document changes, then it will cost only one read. The entire set of documents is cached in memory as long as the query is actively listening to updates, and the SDK will deliver you the cached results in addition to whatever actually changed.
If the query ends and a new one starts up, then you will be charged for the full set of results again.

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.

How much i pay when use onSnapshot firestore?

So when i read realtime data use onSnapshot in collection with 1000 document, i will pay for 1000 read for first time i render the page.
Nah my question is, when someone add 1 new document to that collection, am i get charged only for read 1 new document or i get charged again for read 1000 document + 1 new document ?
I read some post on this site about that, and also i read from firebase website in link below but i not really understand.
https://firebase.google.com/docs/firestore/pricing#listens
This post
Listening to query results
Cloud Firestore allows you to listen to the results of a query and get
realtime updates when the query results change.
When you listen to the results of a query, you are charged for a read
each time a document in the result set is added or updated. You are
also charged for a read when a document is removed from the result set
because the document has changed. (In contrast, when a document is
deleted, you are not charged for a read.)
Also, if the listener is disconnected for more than 30 minutes (for
example, if the user goes offline), you will be charged for reads as
if you had issued a brand-new query.
My question is: when someone adds 1 new document to that collection, am I get charged only for read 1 new document or I get
charged again for read 1000 document + 1 new document ?
The answer is to be found in the second paragraph of the documentation excerpt you mention in your question:
When you listen to the results of a query, you are charged for a read
each time a document in the result set is added or updated.
Listening to an entire collection is totally equivalent to listening to a query (the CollectionReference class extends the Query class), therefore when someone adds a new document to the collection, you are charged only for one document read.

Firestore, fetch only those documents from a collection which are not present in client cache

I am implementing a one-to-one chat app using firestore in which there is a collection named chat such that each document of a collection is a different thread.
When the user opens the app, the screen should display all threads/conversations of that user including those which have new messages (just like in whatsapp). Obviously one method is to fetch all documents from the chat collection which are associated with this user.
However it seems a very costly operation, as the user might have only few updated threads (threads with new messages), but I have to fetch all the threads.
Is there an optimized and less costly method of doing the same where only those threads are fetched which have new messages or more precisely threads which are not present in the user's device cache (either newly created or modified threads).
Each document in the chat collection have these fields:
senderID: (id of the user who have initiated the thread/conversation)
receiverID: (id of the other user in the conversation)
messages: [],
lastMsgTime: (timestamp of last message in this thread)
Currently to load all threads of a certain user, I am applying the following query:
const userID = firebase.auth().currentUser.uid
firebase.firestore().collection('chat').where('senderId', '==', userID)
firebase.firestore().collection('chat').where('receiverId', '==', userID)
and finally I am merging the docs returned by these two queries in an array to render in a flatlist.
In order to know whether a specific thread/document has been updated, the server will have to read that document, which is the charged operation that you're trying to avoid.
The only common way around this is to have the client track when it was last online, and then do a query for documents that were modified since that time. But if you want to show both existing and new documents, this would have to be a separate query, which means that it'd end up in a separate area of the cache. So in that case you'll have to set up your own offline storage on top of Firestore's, which is more work than I'm typically willing to do.

How to avoid redundantly downloading data when using switchMap and inner observables in RxFire?

I have some RxFire code that listens to a Firestore collection query (representing channels) and, for each of the results, listens to a Realtime Database ref for documents (representing messages in that channel).
The problem I'm running into is that the Realtime Database documents are re-downloaded every time the Firestore query changes, even if they're for a path/reference that hasn't changed.
Here's some pseudo-code:
collection(channelsQuery).pipe(
// Emits full array of channels whenever the query changes
switchMap(channels => {
return combineLatest(
channels.map(channel =>
// Emits the full set of messages for a given channel
list(getMessagesRef(channel)),
),
);
})
)
Imagine the following scenario:
Query intially emits 3 Firestore channel documents
Observables are created for corresponding Realtime Database refs for those 3 channels, which emit their message documents
A new Firestore document is added that matches the original query, which now emits 4 channel documents
The previous observables for Realtime Database are destroyed, and new ones are created for the now 4 channels, re-downloading and emitting all the data it already had for the previous 3.
Obviously this is not ideal as it causes a lot of redundant reads on the Realtime Database. What's the best practice in this case? Keep in mind that when a channel is removed, I would like to destroy the corresponding observable, which switchMap already does.

Firebase - Firestore - how many time will I read documents

With the new Firestore from Firebase, I discovered that I have poor knowledge with Observables.
My problem is the following:
I get some data with db.collection('room').
If I don't listen to the observable with a subscription, do I fetch the document? (I think so).
For every change in my collection "room", is it considered as a "new document read" by Firestore?
If I have duplicated Observables which return db.collection('room') in my app, will I have X calls to the Firestore database or just one?
Thanks!
If I don't listen to the observable with a subscription, do I fetch the document? (I think so).
When you call var ref = db.collection('room'), ref is not really an observable it is a reference to the 'room' collection. Creating this reference does not perform any data reads (from network or disk).
When you call ref.get() or ref.onSnapshot() then you are fetching the documents from the server.
For every change in my collection "room", is it considered as a "new document read" by Firestore?
If you are listening to the whole collection (no where() or .orderBy() clauses) and you have an active onSnapshot() listener then yes, you will be charged for a document read operation each time a new document is added, changed, or deleted in the collection.
If I have duplicated Observables which return db.collection('room') in my app, will I have X calls to the Firestore database or just one?
If you are listening to the same Cloud Firestore data in two places you will only make one call to the server and be charged for the read operations one time. There's no cost/performance penalty to attaching multiple listeners to one reference.

Resources