Compund Query with OR operator flutter and firebase [duplicate] - firebase

This question already has answers here:
How to perform compound queries with logical OR in Cloud Firestore?
(12 answers)
Closed 2 years ago.
I am creating a flutter app that should query a Firestore collection and return results if two conditions are met. Here is my code:
Stream<List<FormQuestions>> get question {
return someCollection
.where('myQUestion', isEqualTo: 'nameQuestion')
.snapshots()
.map(_someQuestionListFromSnapshot);
}
If I do with just one .where() condition, it works fine. But with two, it gives no results although I have documents that meet both conditions. I would like it to return for multiple .where() conditions like so:
Stream<List<FormQuestions>> get question {
return someCollection
.where('myQUestion', isEqualTo: 'nameQuestion')
.where('myQuestion', isEqualTo: 'ageQuestion')
.snapshots()
.map(_someQuestionListFromSnapshot);
}
Is there a way to add an OR(||) operator or how can I do this so that I get results for both "nameQuestion" and "ageQuestion"?
Kindly assist. Thanks.

Firestore does not support regular OR queries, but it does support IN queries with which you can implement what you need.
You query would look like citiesRef.where('country', 'in', ['USA', 'Japan']);
someCollection
.where('myQUestion', whereIn: ['nameQuestion', 'ageQuestion'])
Also see:
Firebase Firestore - OR query, which has answers that cover all kinds of variations of OR conditions.
the Flutter reference documentation for the where method

You can do the following:
With getDocuments():
void getData() async {
List<Future<QuerySnapshot>> futures = [];
var firstQuery = someCollection
.where('myQUestion', isEqualTo: 'nameQuestion')
.getDocuments();
var secondQuery = someCollection
.where('myQuestion', isEqualTo: 'ageQuestion')
.getDocuments();
futures.add(firstQuery);
futures.add(secondQuery);
List<QuerySnapshot> results = await Future.wait(futures);
results.forEach((res) {
res.documents.forEach((docResults) {
print(docResults.data);
});
});
}
So here you preform an OR query, and return the result from the two queries. Future.wait(futures) will wait until both of these queries are finished and return a `List that will contain the documents that satisfy these two queries.
With snapshots():
import 'package:async/async.dart' show StreamGroup;
///
getData() async {
List<Stream<QuerySnapshot>> streams = [];
final someCollection = Firestore.instance.collection("users");
var firstQuery = someCollection
.where('myQUestion', isEqualTo: 'nameQuestion')
.snapshots();
var secondQuery = someCollection
.where('myQuestion', isEqualTo: 'ageQuestion')
.snapshots();
streams.add(firstQuery);
streams.add(secondQuery);
Stream<QuerySnapshot> results = StreamGroup.merge(streams);
await for (var res in results) {
res.documents.forEach((docResults) {
print(docResults.data);
});
}
}
snapshots() returns a Stream which is a source of asynchronous data events, so it will keep listening for incoming data. To merge the two or query, you need to use StreamGroup.merge(streams) which merges the events from streams into a single single-subscription stream. Then using await for you can iterate over the events of a stream like the for loop iterates over an Iterable.
Check:
https://dart.dev/tutorials/language/streams

You cannot find a document using both where() calls because you are querying on the same myQUestion property. There is no way a property can hold two values at the same time. It can hold one or the other. You might be looking for an OR operator but using an AND in the way you do, it's not possible. An AND will only work when you query on different properties.

Related

flutter firebase order by in a condition

the probleme in my code is whenever i add the orderby my code stops working and the data is not displaying even when i don't get any errors
here's the stream i sued :
Stream<QuerySnapshot> searchData(String textEntered) async* {
var _search = FirebaseFirestore.instance
.collection("users")
.doc(sharedPreferences!.getString("uid"))
.collection("inventaire")
.doc(widget.model!.InventoryID)
.collection("produits").where('BarCode', isGreaterThanOrEqualTo: textEntered).orderBy('LastUpdate', descending: true)
.snapshots();
yield* _search;
}
I'm guessing now, as there isn't much to go on. But you are probably missing an index for the query.
Check your logs if you get anything like: " The query requires an index."
You can read more about it at:
https://firebase.google.com/docs/firestore/query-data/indexing
Another alternative is using a StreamController to return a modified stream, that way you could refactor the code into a listen:
Stream<QuerySnapshot> searchData(String textEntered) {
var controller = StreamController<QuerySnapshot>();
FirebaseFirestore.instance
.collection("users")
.doc(sharedPreferences!.getString("uid"))
.collection("inventaire")
.doc(widget.model!.InventoryID)
.collection("produits").where('BarCode', isGreaterThanOrEqualTo: textEntered).orderBy('LastUpdate', descending: true)
.snapshots().listen((QuerySnapshot qSnapshot) {
controller.add(qSnapshot);
});
return controller.stream;
}
Using the StreamController could even allow you to map the documents out of the QuerySnapshot returned on the listen callback handler and instead returning a list of PODO objects already mapped as opposed to the QuerySnapshot. My two cents.

Dart firebase AND equivalent

My assumption was multiple where conditions make AND equivalent , as in
.collection("Property")
.where('FirebaseAuthId', isEqualTo: userId)
.where('PropertyId', isEqualTo: PropertId);
but on analysing the firestore DB , this seems to work more like an OR .how can the AND achieved for updating document .
Future updateLastChatVisitTime(userId, PropertId) async {
WriteBatch batch = Firestore.instance.batch();
var _documentRef = await Firestore.instance
.collection("Property")
.where('FirebaseAuthId', isEqualTo: userId)
.where('PropertyId', isEqualTo: PropertId);
_documentRef.getDocuments().then((ds) async {
if (ds != null) {
ds.documents.forEach((value) {
print('MAPPINGS are ${value.documentID}');
batch.updateData(
Firestore.instance
.collection("Property")
.document(value.documentID),
{"LastVisitTime": DateTime.now().toUtc()});
});
await batch.commit().then((value) {
print("Batch updateLastChatVisitTime update ok");
}).catchError((err) {
print('Error updateLastChatVisitTime update $err');
});
}
});
}
Also this link from firebase says it acts like Logical AND , am confused , did I miss something
https://firebase.google.com/docs/firestore/query-data/queries#compound_queries
.where() query can also act as filters - they will not generally include records where a field is missing. It's quite possible your records (which you do not show) are inconsistent; some may be missing one or the other of the fields. I'm not sure what the compound index would create under the circumstances.

How to fetch data from firestore using .where(GreaterThan, and LessThan) condition. - Flutter

I'm trying to fetch data from firestore using the .where() condition.
Query adsRef = FirebaseFirestore.instance
.collection('All ads')
.where('adPrice',
isGreaterThanOrEqualTo: '2000')
.where('adPrice',
isLessThanOrEqualTo: '115000')
.limit(adsPerPage);
QuerySnapshot querySnapshot = await adsRef.get();
When using only one condition (eg: .where('adPrice',isGreaterThanOrEqualTo: '2000') ) works but dosen't fetch all documents[Just fetching random documents].
And when using both condtions it retruns null even though the DB has matching data.
Is there any other way to fetch data from firestore within two numbers? (in between 2000 to 115000)
Two inequality conditions are not valid in one firestore query. wrap you block of code in try catch and you will see the error.
Alternatively you can query both of your conditions separately and then merge the results like this:
void getAds() async {
List<Future<QuerySnapshot>> futures = [];
var firstQuery = FirebaseFirestore.instance
.collection('All ads')
.where('adPrice', isGreaterThanOrEqualTo: '2000')
.getDocuments();
var secondQuery = FirebaseFirestore.instance
.collection('All ads')
.where('adPrice', isLessThanOrEqualTo: '115000')
.getDocuments();
futures.add(firstQuery);
futures.add(secondQuery);
List<QuerySnapshot> results = await Future.wait(futures);
results.forEach((res) {
res.documents.forEach((docResults) {
print(docResults.data);
});
});
}
Please do try this and let me know if it works for you. Thanks

How can I check the length of a firebase document using stream builder

In my flutter firebase app, I am able to get the length of a user's activity document in firebase using a query snapshot. However, I want the number of documents to update in real-time without the user needing to refresh the page. Can I do that by converting the codes below using stream builder to get the real-time length and how can I do that?
this is the code am using now which works perfectly well but doesn't update in real-time.
//this is the code I want to convert to stream
//builder.
static Future<int> numActivities(String userId)
async {
QuerySnapshot userActivitiesSnapshot = await
activitiesRef
.document(userId)
.collection('userActivities')
.where('seen', isEqualTo: '')
.getDocuments();
return userActivitiesSnapshot.documents.length;
}
You need to use the docs property, which "returns a List containing DocumentSnapshot classes", as follows:
return userActivitiesSnapshot.docs.length;
To get a stream of documents, you need to use the .snapshots() method which returns a Stream of QuerySnapshot instead of the .getDocuments() (deprecated in favor of .get()) method which returns a Future of QuerySnapshot.
Then you can map Stream<Snapshot> into a stream of the length of the snapshot's documents.
Your numActivities method should look like this:
static Stream<int> numActivities(String userId) {
return activitiesRef
.document(userId)
.collection('userActivities')
.where('seen', isEqualTo: '')
.snapshots()
.map((documentSnapshot) => documentSnapshot.docs.length);
}
Using this in your use case, you need to listen to the stream and update the _activityCount variable.
_setUpactivityCount() async {
final String currentUserId =
Provider.of<UserData>(context, listen: false).currentUserId;
DatabaseService.numActivities(currentUserId).listen((activityCount) {
if (mounted) {
setState(() {
_activityCount = activityCount;
});
}
});
}
Make sure you take care of _activityCount being null in it's initial state.

Firestore merge 2 queries with Flutter

I am trying to make 2 queries to Firestore and merge the results into one in order to simulate a Firestore OR query.
I segmented my code according to the bloc pattern.
///private method to zip QuerySnapshot streams
Stream<List<QuerySnapshot>> _combineStreams(String userId) {
var stream1 = todosCollection
.where("owners", arrayContains: userId)
.snapshots();
var stream2 = todosCollection
.where("contributors", arrayContains: userId)
.snapshots();
return StreamZip(([stream1, stream2])).asBroadcastStream();
}
///exposed method to be consumed by repository
Stream<List<Todo>> todos(String userId) {
var controller = StreamController<List<Todo>>();
_combineStreams(userId).listen((snapshots) {
List<DocumentSnapshot> documents = List<DocumentSnapshot>();
snapshots.forEach((snapshot) {
documents.addAll(snapshot.documents);
});
final todos = documents.map((doc) {
return Todo.fromEntity(TodoEntity.fromSnapshot(doc));
}).toList();
controller.add(todos);
});
return controller.stream;
}
In my bloc I have the following code that should update my view accordingly my database state but it's not working. The database insertion work but the view doesn't refresh and I don't know why.
_gardensSubscription?.cancel();
_gardensSubscription = _dataRepository.gardens(event.userId).listen(
(gardens) {
dispatch(
GardensUpdated(gardens),
);
},
);
I am not very confortable with Stream and particularly with StreamController process. Is it possible to do this task more easily?
Time to use the great powers of RxDart: https://pub.dev/packages/rxdart
You can't do all types of streams transformations with this lib.
For example, you can use the merge operators to achieve exactly what you want

Resources