Reducing the number of reads firestore - firebase

I have a dating kind of app (without chat support) in which I am showing list of all the profiles that matches certain criteria to user. I have used realtime snapshot listener for this.
query.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
return;
}
if (snapshot != null && !snapshot.isEmpty()) {
List<FeedDetails> feedDetailsList = new ArrayList<>();
for (DocumentSnapshot document : snapshot.getDocuments()) {
FeedDetails feedDetails = document.toObject(FeedDetails.class);
feedDetailsList.add(feedDetails);
}
feedItemAdapter.updateData(feedDetailsList);
}
}
});
In individual profile doc, I have 10-12 field with one online/offline info field. So lets say if no other field changed but online/offline status changed for some profiles then listener is going to read that document again. Is there any way to cut down those reads?

I have a 10-12 field with one online/offline info field. So let's say if no other field changed but online/offline status changed for some profiles then the listener is going to read that document again.
There is no way in Cloud Firestore in which you can listen only to a set of properties of a document and exclude others. It's the entire document or nothing. So if this field online=true is changed into online=false, then you will get the entire document.
Cloud Firestore listeners fire on the document level and always return complete documents. Unfortunately, there is no way to request only a part of the document with the client-side SDK, although this option does exist in the server-side SDK's select() method.
If you don't want to get notified for specific fields, consider adding an extra collection with documents that will contain only those fields. So create that additional collection where each document just contains the data you don't need. In this way, you won't be notified for online/offline changes.

Related

Upsert in Firebase Firestore without the onFailure callback

There must be a better way to make upsert in Firebase Firestore in Kotlin.
I have collection of users that contains another collection userDocuments that contains field called highlights containing list of highlights.
I cannot use set and merge options as that will override the highlights list.
Any ideas how to make the code better. I do not like making two database requests on create and handling the failure like this. Maybe my database structure can be also optimized but I thought it is smart as all private userData will be stored in users collections with some subcollections.
My database structure is like this:
users -> {userId} -> userDocuments -> {docId} -> highlights ["this will be highlighted"]
users, and userDocuments are collections. Highlights is a field on userDocument.
docId might not yet be there, there will be 1000 of documents. And I do not want to add it to every user. I want it to be there, only when they make a change such as add or remove highlight to list of highlights.
usersCollection
.document(userId)
.collection("userDocuments")
.document(docId)
.update("highlights", FieldValue.arrayUnion(text))
.addOnFailureListener { err ->
// TODO should be handled differently
if (err is FirebaseFirestoreException &&
err.code === FirebaseFirestoreException.Code.NOT_FOUND
) {
val highlights = listOf(text)
usersCollection
.document(it)
.collection("userDocuments")
.document(docId)
.set(mapOf("highlights" to highlights), SetOptions.merge())
}
}
You can update using dictionary notation or dot notation too.
https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects
db.collection("userDocuments")
.document(docId)
.update({
"highlights": FieldValue.arrayUnion(text)
});
You can consider using transactions as I mentioned in the comment above. But not sure if that is what you are looking for.

Firebase Firestore Data Not Visible Issue

I ran into issue where Firestore is not reflecting data on client.
Lets say when I create cart manually from Firebase Console it reflects on client side but when I create Cart from client side it does not reflects, although a empty card appears but its null. Assist me on this
Firestore Rules are Public
Data Calling Method
public async Task<ObservableCollection<T>> GetCollection(string collection)
{
var tcs = new TaskCompletionSource<ObservableCollection<T>>();
await DataStore.Collection(collection).Get()
.AddOnCompleteListener(new OnCollectionCompleteListener<T>(tcs));
return await tcs.Task;
}
Thanks
I resolved this issue on my own. While working with Firestore, I understood that if you keep any field null in Firestore, the data inside the document will not be visible. So make sure to not leave any field empty.

flutter local notification on firestore change

In my firestore I have a list of documents which represent locations on a map.
I would like to show a local notification not only when a new document is created in the database, but also when the location is within a certain distance from my current location.
At the moment I have a streambuilder which loads the position into my local map, and a streamlistener to give a notification on a new document:
CollectionReference loc =
FirebaseFirestore.instance.collection('locations');
late Stream<QuerySnapshot> _locStream;
late StreamSubscription<QuerySnapshot> streamSub;
#override
void initState() {
super.initState();
_locStream = loc.snapshots();
streamSub = _locStream.listen((data) {
final snackBar = SnackBar(
content:
Text('New location added!'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
});
}
the problem is that the stream is returning ALL the documents, not only the new one, so I have no idea how to "isolate" the new one and, more important, how to get its value in order to compare it with my current location.
Is that possible to achieve so?
A Firestore QuerySnapshot always contains all information that fits within the query. But when the QuerySnapshot is an update on an existing listener/stream, it also contains metadata about what changes compared to the previous QuerySnapshot on the listener/stream.
To get access to this metadata, use the QuerySnapshot's docChanges property, rather than docs, and check the DocumentChangeType of the type property of each change to find only the documents that were added. In the initial snapshot that will be all of the documents, since all of them are new to the snapshot at that point.
See the Firebase documentation on viewing changes between snapshots

Create documents, sub collections in Firestore via flutter on screen loads

I want to achieve is when flutter screen loads a document should create in firestore in following order.
Document > Sub Collection > Document > Data Fields
I manage to create documents and sub collections in above order, but the first Document appear in italic. That's because the child collection, documents creating before parent document created.
But I couldn't able to fix the issue. I've modified the code now it's not even creating the document. Before this It created in italic mode. Now it's not at all.
Here is the code.
getCurrentUser().then((user) {
DocumentReference todayReference = firestoreInstance.collection('attendance').document(todayDate);
firestoreInstance.collection('profiles').where('user_id', isEqualTo: user).snapshots().listen((onData) {
onData.documents.forEach((f) {
CollectionReference todaySubCollection = todayReference.collection(f.documentID);
DocumentReference attendanceReference = todaySubCollection.document(f["name"].toString().toLowerCase());
Map<String,dynamic> mapData = new Map<String,dynamic>();
mapData['attendance_status'] = true;
mapData['in'] = true;
mapData['out'] = true;
firestoreInstance.runTransaction((transaction) async {
await transaction.set(attendanceReference, mapData);
});
});
});
});
Here getCurrentUser() is returning the logged in user id.
Each profiles assigned to a user.
So, What I'm trying to do is, once user logged in a document should create under attendance collection named today's date.
Then looping through each profiles where user_id is matched with logged in user, the matching results will be store as sub collection under today's date with profiles name field.
Then under the name (document), a transaction needs to run to set details like attendance_status, in & out.
Following images will show how previously documents created.
I need to find a way to create documents, collection without in italic mode. Any help would be appreciated.
"Italicized" documents are virtual/non-existent as mentioned in the docs. If a document only has a sub-collection, it will be a virtual/non-existent document. A workaround for this is by writing fields in the document, like what you've mentioned in the comments.

Is it possible to only retrieve the newest document with a QuerySnapshot in Firestore?

I have a question regarding QuerySnapshot. For example, lets say I have a chat app. To keep the discussion updated I use a StreamBuilder connected to Firestore. I use a querySnapshot to retrieve all the documents in the collection "messages" and every time I add a message a new Query Snapshot is triggered with the new message and all the previous documents. So here is my question, If my collection "messages" contain 10 documents, the first time I have to get all document so I read 10 documents. Next I add a messag, I now have 11 documents in my collection and the querySnapshot will then return the 11 documents even if I only need the new one. So in the end, will it count as 11 documents read (10 + the new one ) or 21 (10 +11 ) ? If it is the lattest, is there a way to only get the new document instead of all the documents ?
Thanks in advance.
It depends if you have setup a listener .addSnapshotListener or if you have just used .getdocument. If you have set up a listener it will read only new or changed documents from Firestore and then merges it with the locally cached data. It will then present your app with the full 11 documents again though. 10 from the local cache, 1 loaded new. You do have the option to only get the changes, see below. If you didn't set up a listener you can just changed .getdocument with .addSnapshotListener, the rest of the code should be the same. Don't forget to detach the listener when it"s not longer needed though.
db.collection("cities").whereField("state", isEqualTo: "CA")
.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
print("New city: \(diff.document.data())")
}
if (diff.type == .modified) {
print("Modified city: \(diff.document.data())")
}
if (diff.type == .removed) {
print("Removed city: \(diff.document.data())")
}
}
}
For as long as a listener is attached to a query, no documents will be re-read from the server as long as they are unchanged. The listener is delivered the entire set for every change, but they are being delivered from memory if they are not changed. You are only getting the changes with each callback, and you can even check to see exactly what changed each time.

Resources