Compound queries in Firestore - different fields in "order by" and "where" - firebase

Will this work in Firebase Firestore?
var result = await firestoreInstance
.collection('messages')
.where('place', whereIn: ["value1","value2","value3","value4","value5","value6","value7","value8","value9"])
.orderBy('dateAdded')
.startAfterDocument(lastDocument)
.limit(documentLimit)
.getDocuments();
I mean, I want to filter data based on IN condition on one field ('place'), and then paginate it based on another field ('dateAdded').
Will it use a composite index on fields 'place' and 'dateAdded'?
I Firestore allows it, will it work in Flutter SDK?

Should work just fine - I already do similar actions in my code. One thing I found by trying - paging back with an "EndBeforeDocument" call seems to need to reverse the orderBy, so it will need to setup multiple compound indexes. A link to a page to create those indexes for you will be returned in the "err" from the .get() call (so set up your callbacks appropriately to catch this).
So Doug is generally right, btw, bad etiquette to ask questions without trying (and/or searching StackOverflow) - you just got lucky because I just wrote my own paging interface for a library for my application. While I intend to release the interface as an NPM package shortly, it probably won't help you - I'm using the Web API (javascript) under React.

Related

Flutter Firebase local change doesn't update listener stream

I'm relying on Firebase Firestore offline capabilities, so I'm not using await on my queries as stated on the Access Data Offline Firebase doc. I'm expecting that when I write something I'll get an immediate reflection on my read stream, however, I'm only getting an update when the server/remote has been updated. Basically:
Update something in the DB. Note, I'm not using await
_db.doc(parentDoc).collection(DocInnerCollection).doc(childDoc).update({
"name": value,
});
I expect my listeners to be updated immediately. Note I've set the includeMetadataChanges to true as stated in the above doc.
_db.doc(parentDoc)
.collection(DocInnerCollection)
.orderBy('start_date', 'desc')
.limitToLast(1)
.snapshots(includeMetadataChanges: true)
.map((snapshot) {
print(snapshot.metadata.isFromCache)
});
However, I get no such update and instead I only get an update when the server has been updated.
You're requesting only one document with .limitToLast(1), yet are not providing a sort order for your query. This essentially means that you'll get a random document from your collection, and the chances of that being the newly updated document are close to zero.
If you want the latest (not just last) document, you need some ordering criteria to determine what latest means. Typically you'd do this by:
Adding a lastUpdated field to your documents, and setting that to firebase.firestore.FieldValue.serverTimestamp().
Ordering your query on that timestamp with orderBy('lastUpdated', 'desc').
And then limiting to the first result with limit(1).

Firebase Firestore - Best way to call a bunch of where gets?

I have an array of user emails that I want to then pull from firebase each of the corresponding user documents from a collection where one of the email matches. I don't want to pull the whole collection as it would be expensive. What is the easiest way to do this? I tried for looping over the array with individual gets to firebase but ran into promise issues as I want to do something in Javascript with them right after.
Thanks so much!
based on what i understood from your question i can only think of using await Promise.all() - you can look into this here.
as an example you could pass an array of promises to await Promise.all() so you could do
const res = await Promise.all(
array.map( x => db.collection('users')
.where('email' , '==', x.email).limit(1).get());
mind you that in this example you would still have to process the result as they will return a snapshot not a document ...
Update:
Hey there, i just noticed that you can use in operator in firebase query, which will return the values matching in a given array.
i'm not sure but maybe using it might be suitable in your use-case you can check the documentation here

flutter firebase querysnapshot: case insensitive method in dart code

I'm flutter user with firebase connected.
I'm trying to call list of users from cloud firestore with querysnapshot when users call search feature and then string submitted. What I've tried is snippet below:
Future<QuerySnapshot> allUsers = usersReference
.where("profileName",
isGreaterThanOrEqualTo: str)
.getDocuments();
setState(() {
futureSearchResults = allUsers;
});}
The problem is I have to make 'profileName' called by querysnapshot 'case insensitive'. For example, profileName 'HELLO WORLD' in cloud firestore must be printed as 'hello world' in my android studio flutter console. So i need a flutterFire method to convert all uppercase characters from cloud firestore to lowercase characters. Unfortunately, i couldn't find simple property to set case-insensitive from flutterFire plugIn.. ]:
Furthermore, what i'm trying to call from firestore is not a bunch of strings but certain form of document which is going to be converted to certain custom class written in dart. It made me unavailable with neat dart methods like toLowerCase().
If anyone know method in dart code to change uppercase characters from cloud firestore to lowercase, please share your knowledge.
Clear answers so appreciated!! Thank you in advance [:
Firestore does not offer case-insensitive queries. If you need to query for strings using any case, you should store a "canonical" version of the string in Firestore, the make the client code use that for all queries. So, for example, you could store all strings as lowercase in the database, then require your client code to convert all strings to lowercase before querying.

The method 'updateData' isn't defined for the type 'Query'. Futter, Cloud Firestore

I would like to update data in a document based on a query. Here is my code:
Firestore.instance
.collection("Categories")
.where("userEmail", isEqualTo: "${user?.email}")
.updateData({"category_budget_remaining": _remainingCategoryBudget2})
.then((value){});
I am getting the following error: The method 'updateData' isn't defined for the type 'Query'
after where() you need to get documents then do updating
where(...).getDocuments().then((val)=>
val.documents.forEach((doc)=> {
doc.reference.updateData({...})
});
});
this code selects and updates all documents that goes true for where condition. If you want do it just for 1 document then just add .limitTo(1) before getDocuments().
As you can see from the API documentation, where() returns a Query, and Query doesn't have a method called updateData(). So, what you're seeing here is no surprise.
Firestore doesn't offer a way to bulk update documents like a SQL "update where" command. What you will have to do is execute the query for the documents to change, iterate the documents in the result set, and update each one individually. Yes, it requires a document read for every document to change, and no, there are no alternatives for this.
For me worked by changing
.updateData
to just
.update
My answer is similar to #Habib Mahamadi but this is the correct and only way to acheive this functionality, my answer is little differente as firebase has changed names of functions.
Firestore.instance
.collection("Categories")
.where("userEmail", isEqualTo: "${user?.email}")
.then((value)=> value.docs.forEach((element){element.reference.update({"" : ""})});

What is the difference between getDocuments() and snapshots() in Firestore?

I am a little confused about the difference between these two. My understanding is that getDocuments is a type of Future and seems to get the entire documents according to the query. while snapshots, on the other hand, is a type of Stream and, correct me if I'm wrong, I think it represents the results of the query? I need a more specific explanation of this issue. I will include some code snippets as an example for more clarification
getDocuments()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).getDocuments();
}
snapshots()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).snapshots();
}
So what's the difference?
When you call getDocuments(), the Firestore client gets the documents matching the query from the server once. Since this may take some time it returns a Future<QuerySnapshot>.
When you call snapshots() the Firestore client gets the documents, and then keeps watching the database on the server for changes that affect your query. So if document is written in the users collection that affects your query, your code gets called again. So this returns a stream of QuerySnapshot.
In both cases the results for the entire query are in the QuerySnapshot object.
I highly recommend reading the Firestore documentation on getting data once and on listening realtime updates. While they don't contain Flutter examples, the explanation in there applies equally to the Flutter libraries.
getDocuments():
It's used to provide data once. Cloud Firestore contains collections and inside these collections, you have documents that may contain subcollections or fields mapped to a value. To retrieve any of the doc fields to used it in widget this is used.
snapshots():
It will be called on every data change in your document query. For this StreamBuilder must be used to fetch fields as modified.
In short, it will do the job of setState() where it gives you the response for every modification so that UI can be updated.

Resources