How to get the number of Firestore documents in flutter? - firebase

I am building a flutter app and using Cloud Firestore. I want to get the number of documents in the database.
I tried
Firestore.instance.collection('products').toString().length
but it didn't work.

Firebase doesn't officially give any function to retrieve the number of documents in a collection, instead you can get all the documents of the collection and get the length of it..
There are two ways:
1)
final int documents = await Firestore.instance.collection('products').snapshots().length;
This returns a value of int. But, if you don't use await, it returns a Future.
2)
final QuerySnapshot qSnap = await Firestore.instance.collection('products').getDocuments();
final int documents = qSnap.documents.length;
This returns a value of int.
However, these both methods gets all the documents in the collection and counts it.
Thank you

With Cloud Firebase 2.0, there is a new way to count documents in a collection. According to reference notes, the count does not count as a read per document but a metaData request:
"[AggregateQuery] represents the data at a particular location for retrieving metadata without retrieving the actual documents."
Example:
final CollectionReference<Map<String, dynamic>> productList = FirebaseFirestore.instance.collection('products');
Future<int> countProducts() async {
AggregateQuerySnapshot query = await productList.count().get();
debugPrint('The number of products: ${query.count}');
return query.count;
}

It Should Be - Firestore.instance.collection('products').snapshots().length.toString();

Since you are waiting on a future, this must be place within an async function
QuerySnapshot productCollection = await
Firestore.instance.collection('products').get();
int productCount = productCollection.size();
Amount of documents in the collection

Future<int> getCount() async {
int count = await FirebaseFirestore.instance
.collection('collection')
.get()
.then((value) => value.size);
return count;
}

Instead of getting all the documents using get() or snapshots() and counting them, we can use Firebase Aggregation Queries. This will provide you with the count.
Here is an example that works in Flutter :
final collection = FirebaseFirestore.instance.collection("products");
final query = collection.where("status", isEqualTo: "active");
final countQuery = query.count();
final AggregateQuerySnapshot snapshot = await countQuery.get();
debugPrint("Count: ${snapshot.count}");
You can find more details here : https://firebase.google.com/docs/firestore/query-data/aggregation-queries

First, you have to get all the documents from that collection, then you can get the length of all the documents by document List. The below could should get the job done.
Firestore.instance.collection('products').getDocuments.then((myDocuments){
print("${myDocuments.documents.length}");
});

Future getCount({String id}) async => FirebaseFirestore.instance
.collection(collection) //your collectionref
.where('deleted', isEqualTo: false)
.get()
.then((value) {
var count = 0;
count = value.docs.length;
return count;
});
this is in dart language...

Above suggestions will cause the client to download all of the documents in the collection to get the count. As a workaround, if your write operations are not frequently happening, you can put the length of the documents to firebase remote config and change it whenever you add/delete documents from the Firestore collection. Then you can fetch the length from firebase remote configs when you need it.

Most Simplest Way :
int count = await FirebaseFirestore.instance.collection('Collection_Name').get().then((value) => value.size);
print(count);

You want to fetch data from firebase so the call will return a Future.
In order to get the int value you have to use the StreamBuilder widget or FutureBuilder.
For example:
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("<collection name>").getStream().snapshot(),
builder: (context, snapshot) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Stack(children: [
Text(
"${snapshot.data!.docs.length}"
)
]));
});
}

Firestore.instance
.collection("products")
.get()
.then((QuerySnapshot querySnapshot) {
print(querySnapshot.docs.length);
});

#2022 Using the recent version of FirebaseFirestore you can now get the count without retrieving the entire collection.
CollectionReference ref = FirebaseFirestore.instance.collection('collection');
int count = (await ref.count().get()).count;

Related

How to retrieve the last added document in my Cloud Firestore collection, using Flutter?

I am currently learning how to use Firebase and it is not too intuitive to me, yet. Basically, I would like to retrieve only the last document in my Cloud Firestore collection and save it as a Map<String, dynamic>. Here is my code:
final _firestore = FirebaseFirestore.instance;
Future<Map<String, dynamic>> fetchData() async {
Map<String, dynamic> collection;
try {
QuerySnapshot<Map<String, dynamic>> data = await _firestore
.collection('collection')
.orderBy('timestamp', descending: true)
.limit(1).get();
collection = data;
} catch (e) {
print(e);
}
return collection;
}
As of my understanding, the QuerySnapshot returns multiple DocumentSnapshots. I thought if I limit it to 1 and sort it by the generated timestamp I would be able to only get the last added document. However, I am unable to save my QuerySnapshot as a Map<String, dynamic> and could not find a way to make it work.
Many thanks in advance to those taking their time answering to this!
Try this:
collection = data.docs.first.data() as Map;

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.

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.

Querying firestore to check if field value exist and Converting Stream into Future?

I have a function which i created to query firestore and checkwhether a phoneNo exist in the collection called 'users'
I want to get a boolean if the phone No already exist in a document field.
How can i do it
Future<bool> phoneRegisterCheck(phone) async{
bool phoneAlreadyRegistered;
print('start');
var result = Firebase.instance.collection('users').where('phoneNo', isEqualTo: phone);
}
I want to wait for the query to finish and then return the result.
When i Use listen method it is not happening right function returns null.
Future<bool> phoneRegisterCheck(phone) async{
bool phoneRegistered;
print('start');
var result = DatabaseService().userCollection.where('phoneNo', isEqualTo: phone);
result.snapshots().listen((val){
val.documents.isEmpty ? phoneRegistered=false:phoneRegistered=true;
});
return phoneRegistered;
}
Since streams are asynchronous then in your code the return statement will be executed before the data is fully retrieved therefore you get null. You should
use await for instead of listen:
Future<bool> phoneRegisterCheck(phone) async{
bool phoneRegistered;
print('start');
var result = DatabaseService().userCollection.where('phoneNo', isEqualTo: phone).snapshots();
await for(var values in result){
values.documents.isEmpty ? phoneRegistered=false:phoneRegistered=true;
}
return phoneRegistered;
}
From the docs:
Streams can be created in many ways, which is a topic for another article, but they can all be used in the same way: the asynchronous for loop (commonly just called await for) iterates over the events of a stream like the for loop iterates over an Iterable.
https://dart.dev/tutorials/language/streams

Resources