firestore using "where" and "orderBy" - firebase

I am trying to run a simple query on Firestore, using both where and orderBy. The query does not work. After some research, I found out that I might need to build index in Firestore. I did that, but once again, this query does not work.
error result
The initial orderBy() field "[[FieldPath([lastTime]), true]][0][0]" has to be the same as the where() field parameter "FieldPath([lastChat])" when an inequality operator is invoked. 'package:cloud_firestore/src/query.dart': package:cloud_firestore/src/query.dart:1 Failed assertion: line 456 pos 13: 'field == orders[0][0]
here is the code:
Stream<QuerySnapshot<Map<String, dynamic>>> chatsStream(String email) {
var result = firestore
.collection('users')
.doc(email)
.collection("chats")
.where("lastChat", isNotEqualTo: "")
.orderBy("lastTime", descending: true)
.snapshots();
return result;
}
and this is index in firestore:
sorry for my english, thanks.

You need to specify an orderBy for the lastChat field, as isNotEqualTo is a range condition (it helps to think of it as <> for that purpose):
var result = firestore
.collection('users')
.doc(email)
.collection("chats")
.orderBy("lastChat")
.where("lastChat", isNotEqualTo: "")
.orderBy("lastTime", descending: true)
.snapshots();
The index on lastTime is ascending, but your query is descending. That means the index doesn't match the query, and the database will return no results.
It should actually also log a warning/error with a direct link to create the index with all fields pre-populated (including the correct sort order for lastTime).

Related

using two queries to get 'data1' OR 'data2' in flutter firebase

I want to get data from firebase only if the user is not saved in the document. Because I need a or-query, which is not possible in Firebase, I decided to use RxDart's CombinedStreams with two steams:
var streamOne = FirebaseFirestore.instance
.collection('jobs')
.where('jobState', isEqualTo: 1)
.where('userOne', isNotEqualTo: FirebaseAuth.instance.currentUser!.uid)
.snapshots();
var streamTwo = FirebaseFirestore.instance
.collection('jobs')
.where('jobState', isEqualTo: 1)
.where('userTwo', isNotEqualTo: FirebaseAuth.instance.currentUser!.uid)
.snapshots();
But my app shows the data even if the current user is in 'userOne' OR 'userTwo'. Is it possible to avoid this and get the data just if the currentUser is not 'userOne' OR 'userTwo'?
Your logic is flawed: if the UID is in userOne, the second query will still return that document, and vice versa. What you want is actually an AND: the documents where the UID is not in userOne and not in userTwo.
Unfortunately though that query also isn't possible on Firestore, as all not-equal conditions in a query must be on the same field.
There is no way to capture your logic in a single query, and you will have to filter the documents fo userOne and userTwo in your application code instead.

Flutter Firebase: get documents where a particular fiels is not null

I am trying to fetch documents where callerId fields does not contain null value
I tries this
final collection =
_firestoreService.instance.collection(TIME_SLOTS_COLLECTION_NAME);
Query query = collection
.where("listenerId", isEqualTo: _userService.user!.firestoreId!)
.where("callerId", isNull: false)
.orderBy("startTime", descending: true);
print("after query");
But it returns nothing. Code after this statement does not run at all. It means after query does not get printed on the console. I am not sure what's the problem?
I tried this
final collection =
_firestoreService.instance.collection(TIME_SLOTS_COLLECTION_NAME);
Query query = collection
.where("listenerId", isEqualTo: _userService.user!.firestoreId!)
.where("callerId", isEqualTo: "caller1")
.orderBy("startTime", descending: true);
print("after query");
It runs but the first one does not. Does anyone know something about this?
From looking at the FlutterFire implementation of isNull:
if (isNull != null) {
assert(
isNull,
'isNull can only be set to true. '
'Use isEqualTo to filter on non-null values.');
addCondition(field, '==', null);
}
So it looks like your condition is invalid, and you should be seeing an error message in your program output. That also explains why the app stops working: the assertion fails, and thus your app crashes.
You can use isNotEqualTo (or >, >= and others) to filter here:
.where("callerId", isNotEqualTo: false)
What I find really helpful in cases such as this is to keep the table of supported data types and their sort order handy.

Cloud Firestore inequality operator exception flutter

while i have been using cloud firestore in my flutter app, strange exception occured.
EDITED
this is my code:
Stream<List<Product>> productsStream(int id) async* {
final k = _db
.collection('products')
.where('category_id', isEqualTo: id)
.where('stock', isGreaterThanOrEqualTo: 1)
.orderBy('order')
.snapshots();
yield* k.map((event) => event.docs
.map((e) => Product.fromJson(
e.data(),
))
.toList());
Here what i would like to achieve is to check for a product wether it is in stock and then to order products in an ascending order by order field in my products collection.
But i am receiving this strange error:
Exception:
'package:cloud_firestore/src/query.dart': Failed assertion: line 421 pos 16: 'field == orders[0][0]': The initial orderBy() field '[[FieldPath([order]), false]][0][0]' has to be the same as the where() field parameter 'FieldPath([stock])' when an inequality operator is invoked.
What might be solution?
This is explained in the ordering limitations documentation:
If you include a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field
So I suspect you should have:
.where('category_id', isEqualTo: id)
.where('stock', isGreaterThanOrEqualTo: 1)
.orderBy('stock')
.orderBy('order')
Obviously that means it's no longer primarily ordered by order. You'd need to do local sorting if that's a problem - in which case you may find you don't want to order server-side at all.
Although "not equal to" filters aren't mentioned in the documentation, it sounds like they also count as range comparisons in terms of prohibiting filtering.
So basically, I would suggest you either need to filter locally on stock, or you need to order locally on order - you can't do both in the same Firestore query at the moment. Note that this server-side limitation is something that could change in the future (either for all range comparisons, or possibly just to allow "not equal to") so it may be worth retesting periodically.
You may need to do these two things. (it worked for me as shown in the example down below).
1 - Add an index for stock as Ascending in the console. This index should be in ascending.
2 - You must query based on that index.. So, as opposed to doing
...
.where('stock', isGreaterThanOrEqualTo: 1)
...
You should be doing
...
.orderBy('stock', descending: false)
.where('stock', isGreaterThanOrEqualTo: 1)
...
This applies to all other composite indexes that have equality checks. So you may need to do that for category_id
Also, here's my code for example
For context. I'm displaying a chat list, where the number of messages are greater than 0, and of-course, where the current user is a participant
1 - In GCP https://console.cloud.google.com/firestore/indexes/composite?project=**your-project-name**
I added the required index as such
Composite index setup
2 - Then finally, in my flutter code
static Future<QuerySnapshot> getUsersChatsList(String userId,
{DocumentSnapshot? startAfterDoc, int limit = 10}) =>_chatsRef
.where('participants_ids', arrayContains: userId)
.orderBy('message_count', descending: false)
.where('message_count', isNotEqualTo: 0)
.orderBy('last_message_created_at', descending: true)
.limit(limit)
.get();
Ignore the limit & startAfterDoc as that was done for pagination
NOTICE that I had to manually order message_count in ASC before checking the condition.
And finally, of course, they're sorted in DESC in time.
You should make sure that you are ordering with the same constraint as your where like this:
getInventory() async {
String value = user.uid;
return inventoryFirebaseReference
.orderBy('availableStock')
.where('userFirebaseId', isEqualTo: value)
.orderBy('availableStock')
.where('availableStock', isGreaterThan: 0)
.snapshots()
.listen((event) {
chargeProducts = false;
final _documentList = event.docs;
//print(_documentList.length);
_documentList.map((e) => {});
List<ProductMoof> listProductsProv = _documentList.map((doc) {
ProductMoof inventoryItemProv =
ProductMoof.fromMap(doc.data() as Map<String, dynamic>);
inventoryItemProv.firebaseId = doc.id;
return inventoryItemProv;
}).toList();
listInventory = listProductsProv;
notifyListeners();
print('Update gotten from Menu');
});
}

Flutter & Firestore: How do I get the most recent first?

How do I get the most recent first from Firestore?
FirebaseFirestore.instance.collection(Strings.factsText)
.where(
Strings.category,
whereIn: [Strings.climateChange])
.orderBy('timestamp', descending: true)
.snapshots(),
...
final factsDocs = snapshot.data.documents;
return FactsListContainer(factsDocs: factsDocs);
});
The issue appears to be with .where when using with .orderBy!
If you have a createdAt field with a timestamp, or an otherwise always incrementing value, you can get the snapshots in descending order of that field with:
stream: FirebaseFirestore.instance
.collection(Strings.factsText)
.orderBy('timestamp', descending: true)
.where(
Strings.category,
whereIn: [Strings.climateChange])
.snapshots(),
Also see the FlutterFire documentation on the Query.orderBy method.
I finally solved it by creating an index in the Firebase console and this worked perfectly so now I'm getting the latest first.
In my database I went to Indexes to create a new index, added my collection name, and then for the fields I added 'category' - descending and 'timestamp' - descending, clicked Collection and then Create Index.

Unable to get the documents after creating a query index

Code:
var querySnapshot = await Firestore //
.instance
.collection('collection')
.where('name', isEqualTo: ['foo'])
.orderBy('time')
.limit(1)
.getDocuments();
print('${querySnapshot.documents}'); // prints []
It returns empty List.
Database structure:
Index built
Indexing isn't an issue here. Given the query and document you're showing, I'd always expect it to return no results. You're using an array-contains type query on a field that isn't an array. Your name field is a string, and strings can't be matched by array-contains queries.
If you intended for name to be an array, you'll need to modify the document so that it is actually an array with the string "foo" in it.

Resources