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

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.

Related

firestore using "where" and "orderBy"

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).

Firebase Firestore Getting documents again on query stream

FirebaseFirestore.instance
.collection(
'chats/${site}/conversations/${room.id}/messages')
.orderBy('createdAt', descending: true)
.where("createdAt", isGreaterThan: dateTime )
.snapshots()
.map(
(snapshot) {
So, On the first document that inserted to the firestore, the I get i a snapshot. On the second, the stream return the first and the second,
So the i get -
(Doc A)
(Doc A,Doc B)
(Doc A, Doc B, Doc C)
And so on. Is there a way to get:
(Doc A)
(Doc B)
(Doc C)
?
I reviewed your snippet and it appears you are using a Stream from the snapshot() method of a CollectionReference type. According to the documentation, this will stream events as they happen in Firestore. You mentioned that with each document inserted in Firestore, you also started getting the previous documents that were inserted before, instead of getting only the one which was just inserted (the latest). This might be related to the dateTime variable you are using to filter documents. Since you are using a greater than comparison, any documents created after the time set in the dateTime will be returned from the query. This could explain why your query returns additional documents each time a new one is added with a timestamp after the dateTime variable.
If you would like to get only the latest document added to the database each time, you can make use of a query limiter. I tested the limitToLast method to get only the latest document added and it appears to work in my testing. This method returns the very last document in a query, and in order for this to be the newest you would have to invert the process to order by ascending (oldest first) so that the newest document is at the bottom:
FirebaseFirestore firebase = FirebaseFirestore.instance;
firebase
.collection('users')
.orderBy('createdAt', descending: false) // orders by ascending order, latest document is the last
.limitToLast(1) // gets the last document, you can set how many docs to get
.get()
.then((QuerySnapshot snapshot) {
if (snapshot != null) {
// Data is available
snapshot.docs.forEach((doc) {
print(doc['firstName']);
});
} else {
print("No data found");
}
}
for everyone who reach this issue on 2022, the solution is rather simple.
You can stay with the same query but check the doc changes:
snapshot.docChanges.forEach((docChange) {
final data = docChange.doc.data() as Map;
LimitToLast won't solve your problem if the internet connection was down for a few moments and multiple updates arrived, but docChanges is all the changes since the last snapshot.
Note: You need to ignore the first time because it will return all the docs on the collection at the first time.

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');
});
}

Trying to filter down chat messages using Flutter and Firestore

I'm trying to create a Blocked Users list in my chat app using the latest versions of Flutter Beta channel (1.23.0-18.1.pre) and cloud_firestore 0.14.3.
Here's my data structure:
At first, I tried something like this (Hardcoded just to test), by filtering the messages I'm querying from Firestore. Firebase doesn't like this.
query: firestore.collection('messages')
.where('userId', whereNotIn: ['123456789', '987654321'] )
.where('hashtag', isEqualTo: hashTag)
.orderBy('submittedAt', descending: true),
reverse: true,
I get this error:
E/FLTFirestoreMsgCodec(24331): java.lang.IllegalArgumentException: Invalid query. You have an inequality where filter (whereLessThan(), whereGreaterThan(), etc.) on field 'userId' and so you must also have 'userId' as your first orderBy() field, but your first orderBy() is currently on field 'submittedAt' instead.
After doing some more reading, filtering on the client-side by just hiding the messages actually better suits my needs.
Unfortunately, I'm running in circles. I'm currently thinking I would map a stream to a list, and then do something like this:
if (message.userId is in the list) {
isBlocked = true;
} else {
isBlocked = false;
}
And then filtering out the messages if isBlocked is true. I tried hardcoding the values for that and it worked. BTW, Sorry for the pseudocode, but I deviated so many times that now I'm simply lost.
I was wondering if this was the correct approach? Any suggestions would be rad. I also tried using a future list from a stream but I couldn't get that to work either.
Future<Stream<List<BlockedUser>>> getBlockedIds() async {
Stream<List<BlockedUser>> list;
Stream<QuerySnapshot> snapshot = FirebaseFirestore.instance.collection('user').doc('id').collection('blocked').snapshots();
list = snapshot.map((query) => query.docs.map(
(doc) => BlockedUser(
id: doc.data()['id'])
).toList());
return list;
}
I can't get that to work since I don't know what to do with that list.
Thanks, everyone!

Error using orderBy and startAfterDocument in a query

When using orderBy and startAfterDocument in the same query, I get the following error:
failed: Status{code=FAILED_PRECONDITION, description=The query requires an index. You can create it here: https://console.firebase.goog...
This error is being caught when I try to receive the next set of documents.
Tried to create an index using the link provided in the error but it's a single index using the 'finalTimeStamp' field is being generated and firestore throws out the following exception.
this index is not necessary, configure using single field index controls
In my single field indexes, the descending index for a collection scope is already enabled.
My code:
if (_lastVisible == null) {
try {
data = await chatList
.document(widget.currentUserId)
.collection('inbox')
.orderBy('finalTimeStamp', descending: true)
.limit(10)
.getDocuments();
} catch (e) {
print('caught error 1');
}
} else {
try {
data = await chatList
.document(widget.currentUserId)
.collection('inbox')
.orderBy('finalTimeStamp', descending: true)
.startAfterDocument(_lastVisible)
.limit(10)
.getDocuments();
} catch (e) {
print('caught error 2');
}
}
The orderBy and startAfter or startAfterDocuments should be performed on the same field.
In my question above I've performed orderBy on the finalTimeStamp field and was using startAfterDocument which considers the document ID while ordering the documents. Hence, firestore throws an error.
You can solve this issue in 2 ways:
You can perform startAfter() on finalTimeStamp field. (Didn't consider this for my case because I though there may be multiple documents with the same finalTimeStamp. Using this could give out wrong results)
I created a chatRef field in my documents referencing the same document. I then created an index in firestore to order my documents by finalTimeStamp first and chatRef later. Now while querying, I performed orderBy() twice on finalTimeStamp and chatRef along with the startAfterDocument().

Resources