Dart firebase AND equivalent - firebase

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.

Related

Flutter Firestore Update Where

I'm trying to run a query that retrieves a single row given a where clause and updates it. I understand that Firebase doesn't support an UpdateWhere operations so I'm trying to use a Transaction instead.
I'm having difficulty making it work, maybe I'm too used to sql dbs... Here's my broken code
try {
final whereQuery = _db
.doc(userPath(user))
.collection("someInnerCollection")
.where("active", isEqualTo: true)
.limit(1);
await _db.runTransaction((transaction) async {
final entry = await transaction.get(whereQuery); // This doesn't compile as .get doesn't take in a query
await transaction.update(entry, {
"someValue": "newValue",
});
});
} catch (e) {
...
}
From the test I’ve made, I would suggest the following to achieve what you mention:
Based on the following answer:
As you can see from the API documentation, where() returns a Query object. It's not a DocumentReference.
Even if you think that a query will only return one document, you still have to write code to deal with the fact that it could return zero or more documents in a QuerySnapshot object. I suggest reviewing the documentation on queries to see examples.
After doing the query consult, you have to get the DocumentReference for that given result.
Then, you can use that reference to update the field inside a Batched writes
try {
final post = await firestore
.collection('someInnerCollection')
.where('active', isEqualTo: true)
.limit(1)
.get()
.then((QuerySnapshot snapshot) {
//Here we get the document reference and return to the post variable.
return snapshot.docs[0].reference;
});
var batch = firestore.batch();
//Updates the field value, using post as document reference
batch.update(post, { 'someValue': 'newValue' });
batch.commit();
} catch (e) {
print(e);
}
You are passing the DocumentSnapshot back in the update() operation instead of DocumentReference itself. Try refactoring the like this:
final docRefToUpdate = _db.collection("colName").doc("docId");
await _db.runTransaction((transaction) async {
final entry = await transaction.get() // <-- DocRef of document to update in get() here
await transaction.update(docRefToUpdate, {
// Pass the DocumentReference here ^^
"someValue": "newValue",
});
});
You can use a collection reference and then update single fields using .update().
final CollectionReference collectionReference = FirebaseFirestore.instance.collection('users');
await collectionReference.doc(user.uid).collection('yourNewCollection').doc('yourDocumentInsideNestedCollection').update({
'singleField': 'whatever you want,
});
Same code using "where"
collectionReference.doc(user.uid).collection('yourNewCollection').doc().where('singleField', isEqualTo: yourValue).update({
'singleField': 'whatever you want,
});

Admin access with flutter - hide and show widget and button based on User right firebase

I am working to build an admin access to a client. Among the visibility I need to constraint is the visibility of the button.
When changing access to user to admin, the button is not appearing back. The dependent boolean condition is mentioned below.
bool _show = false;
void showFloationButton() {
setState(() {
_show = true;
});
}
void hideFloationButton() {
setState(() {
_show = false;
});
}
void userAdminAccess() async {
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
if ( currentUser != null) {
Firestore.instance
.collection('Users')
.where('isAdmin', isEqualTo: true);
} return showFloationButton();
}
Your code looks like it wants to perform a query, but it is not actually doing so. It's just building a Query object. You have to use get() to actually make that query happen, and await the response:
var querySnapshot = await Firestore.instance
.collection('Users')
.where('isAdmin', isEqualTo: true)
.get();
if (querySnapshot.size > 0) {
// there is at least one document returned by this query
}
else {
// there are not matching documents
}
I suggest learning more about how to perform queries in the documentation.
Note that what you're doing is potentially very expensive. It seems to me that you should probably get a single document for the user, identified by their UID, and look for a field within that document. Getting all admins could incur a lot of reads unnecessarily.

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.

Compund Query with OR operator flutter and firebase [duplicate]

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.

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