How to make a one-time simple query with Firebase Firestore? - firebase

In Dart/Flutter and learning Firebase Firestore... I'm using the following method to test before creating UI:
_testFireStore() async {
var result = Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1')
.snapshots();
await for (var snapshot in result) {
for (var user in snapshot.documents) {
print('main.DEBUG: ' + user.data.toString());
}
}
}
It works as expected -- the print statement is executed initially, but also subsequently in real-time every time any field is updated in the document in the Firestore database.
How can this code be changed such that the snapshot is only retrieved once -- not "subscribed/listened" to... and thus we don't waste bandwidth on unwanted/unneeded data and the print statement is only executed once?

Firestore.instance.collection(...).where(...) returns a Query object. It has a method called getDocuments() that executes the query and gives you a Future with a single set of results.
var query = Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1');
query.getDocuments().then((QuerySnapshot snapshot) {
// handle the results here
})
Or use await to get the QuerySnapshot, since getDocumets() returns a Future.

Use getDocuments(), to retrieve all the documents once:
_testFireStore() async {
var result = await Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1')
.getDocuments();
print(result.documents.toString());
}

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

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.

Resources