Flutter :How to pass List to Firestore where condetion - firebase

How to pass a List as param to firestore where condition? To get all data meets the condition
ie:-
List<String> topicList ;
getData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
topicList = pref.getStringList("topicSymbol");
}
Stream<QuerySnapshot> getStreamQuery(String collPath) {
List<String> filters ;
return db
.collection('$collPath')
.where('Symbol', isEqualTo: getData()) // <---- the problem is here
.snapshots();
}
Thank you in advance

There are a few problems with this approach: firstly getData is an async function and you are not awaiting to get the return list from get data. You want to call await getData() before the return call in getStreamQuery.
Secondly, you should use .where('Symbol', isEqualTo: topicList) in order to compare the lists (this is because getData() returns null)
Let me know if this works for you!

Since you're using await in there, your getData() actually returns a Future - and you're failing to deal with that when calling it.
The simplest fix is to make getStreamQuery async too:
getData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
return pref.getStringList("topicSymbol");
}
Future<Stream<QuerySnapshot>> getStreamQuery(String collPath) async {
List<String> filters ;
return db
.collection('$collPath')
.where('Symbol', isEqualTo: await getData())
.snapshots();
}
When you call getStreamQuery, you'll need to use await there tool.

Related

How to await inside a stream while querying data from Firebase firestore

For context I'm using Getx state management for flutter and i need to call list.bindStream(availabilityStream()) on my Rx<List<Availability>> object.
here is my availabilityStream method
static Stream<List<Availability>> availabilityStream() {
return FirebaseFirestore.instance
.collection('availability')
.where('language',
isEqualTo: GetStorageController.instance.language.value)
.snapshots()
.map((QuerySnapshot query) {
List<Availability> results = [];
for (var availablity in query.docs) {
availablity["cluster"].get().then((DocumentSnapshot document) {
if (document.exists) {
print("Just reached here!");
//! Ignore doc if cluster link is broken
final model = Availability.fromDocumentSnapshot(
availabilityData: availablity, clusterData: document);
results.add(model);
}
});
}
print("result returned");
return results;
});
}
the cluster field on my availability collection is a reference field to another collection. The problem here is i need to await the .get() call to my firestore or the function returns before the data gets returned. I can't await inside the map function or the return type of Stream<List> changes. so how can i await my function call here?
using the advice i got from the comments I've used Stream.asyncMap to wait for all my network call futures to complete.
Here is my updated Repository
class AvailabilityRepository {
static Future<Availability> getAvailabilityAndCluster(
QueryDocumentSnapshot availability) async {
return await availability["cluster"]
.get()
.then((DocumentSnapshot document) {
if (document.exists) {
//! Ignore doc if cluster link is broken
final model = Availability.fromDocumentSnapshot(
availabilityData: availability, clusterData: document);
return model;
}
});
}
static Stream<List<Availability>> availabilityStream() {
return FirebaseFirestore.instance
.collection('availability')
.where('language',
isEqualTo: GetStorageController.instance.language.value)
.snapshots()
.asyncMap((snapshot) => Future.wait(
snapshot.docs.map((e) => getAvailabilityAndCluster(e))));
}
}
How i think this works is that the normal .map function returns multiple promises form the getAvailabilityAndCluster() method then all of the processes that execute asynchronously are all put to Future.wait() which is one big promise that waits all the promises inside it to complete. Then this is passed onto .asyncMap() which waits for the Future.wait() to complete before continuing with its result.

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

Flutter wait for Firestore to complete

I am using flutter with firebase to create an app. The function below gets some data from my firebase database, but the problem is that it takes too long to get the data. How can make sure the app waits for the firebase getting data to finish first before proceeding?
I would like to do something like an await, but I don't know if I can do:
await Firestore.instance...
Code:
void getData() {
Firestore.instance
.collection('collection')
.document('document')
.get()
.then((DocumentSnapshot ds) {
var count = ds.data.length;
for(var i = 0; i < count; i ++){
Firestore.instance
.collection('collection')
.document('document')
.get()
.then((DocumentSnapshot dss) {
// do something
});
}
});
}
To use await, you have to make your function async :
Future<void> getData() async {
Then yes, you can do
var result = await Firestore.instance.collection...
instead of handling the Future result in the then() callback. The next line will not be executed until the Future is resolved.
You will need to await for the firestore to return data.
and then return it.
Future getData() async {
DocumentSnapshot ds = await Firestore.instance
.collection('collection')
.document('document')
.get();
final data = ds.//do something with document snapshot
return data;
}
Hope this helps.

Retrieve Document content from Firebase in Flutter

I'm trying to retrieve user data from a Firebase Collection.
This is what it looks like:
This is the method I wrote to get the data:
static String getUserData(creatorId, keyword) {
var documentName = Firestore.instance
.collection('users')
.document(creatorId)
.get()
.then((DocumentSnapshot) {
String data = (DocumentSnapshot.data['$keyword'].toString());
return data;
});
}
The method only returns null. If I print the String in the Method it works. How can I return the String?
Help would be greatly appreciated.
Cheers Paul
You need to use async and await to be able to wait for the data to be fully retrieved and then you can return the data.
The async and await keywords provide a declarative way to define asynchronous functions and use their results.
For example:
Future<String> getUserData(creatorId, keyword) async {
var documentName = await Firestore.instance
.collection('users')
.document(creatorId)
.get()
.then((DocumentSnapshot) {
String data = (DocumentSnapshot.data['$keyword'].toString());
return data;
});
}
And then you since getUserData returns a Future, you can use the await keyword to call it:
await getUserData(id, key);
https://dart.dev/codelabs/async-await#working-with-futures-async-and-await

Flutter Return Length of Documents from Firebase

Im trying to return the length of a list of documents with this function:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
respectsQuery.getDocuments().then((data) {
var totalEquals = data.documents.length;
return totalEquals;
});
}
I'm initialize this in the void init state (with another function call:
void initState() {
totalLikes(postID).then((result) {
setState(() {
_totalRespects = result;
});
});
}
However, when this runs, it initially returns a null value since it doesn't have time to to fully complete. I have tried to out an "await" before the Firestore call within the Future function but get the compile error of "Await only futures."
Can anyone help me understand how I can wait for this function to fully return a non-null value before setting the state of "_totalRespsects"?
Thanks!
I think you're looking for this:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
var querySnapshot = await respectsQuery.getDocuments();
var totalEquals = querySnapshot.documents.length;
return totalEquals;
}
Note that this loads all documents, just to determine the number of documents, which is incredibly wasteful (especially as you get more documents). Consider keeping a document where you maintain the count as a field, so that you only have to read a single document to get the count. See aggregation queries and distributed counters in the Firestore documentation.
Perfect code for your problem:
int? total;
getLength() async {
var getDocuments = await DatabaseHelper.registerUserCollection
.where("register", isEqualTo: "yes")
.get();
setState(() {
total = getDocuments.docs.length;
});
}
#override
void initState() {
super.initState();
getLength();
if (kDebugMode) {
print(total);
}
}

Resources