I want to calculate how many documents are in a collection, not the length of the document. I have tried it with some code but what appears is the length of the character from my document name.
this my code :
StreamSubscription<DocumentSnapshot> userpost;
final DocumentReference documentReference =
Firestore.instance.document("product/$documentPost");
userpost = documentReference.snapshots().listen((datasnapshot) {
if (datasnapshot.exists) {
for (int i = 0; i < datasnapshot.data.length; i++){
print(datasnapshot.data.length);
}
An Example Function to fetch Documents Count.
void countDocuments() async {
QuerySnapshot _myDoc = await Firestore.instance.collection('product').getDocuments();
List<DocumentSnapshot> _myDocCount = _myDoc.documents;
print(_myDocCount.length); // Count of Documents in Collection
}
You can use the count() function which was added in cloud_firestore version 4.0.0
Accepted answer might be a bad solution because you have to fetch all the documents just to count the number of documents. As per Firestore pricing, every document read is taken as 1 read count.
So a better solution is to use the count() function instead.
AggregateQuerySnapshot query = FirebaseFirestore.instance.collection('random_collection').count().get();
int numberOfDocuments = query.count;
count() is an Aggregation Query
PS: You might need to update your firebase plugins in pubspec.yaml.
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>> userList = FirebaseFirestore.instance.collection('users');
Future<int> countUsers() async {
AggregateQuerySnapshot query = await userList.count().get();
debugPrint('The number of users: ${query.count}');
return query.count;
}
Related
How to get multiple documents from a Collection in firebase if we want to filter using not_in operator with a uniqueId list of documents.
I have a arrayList like this:
ArrayList<String> idList = new ArrayList();
idList.addAll(uniqueIdList);
// now idList have more than 500 uniqueId
Query query = db.collection("my_collection")
.whereEqualTo("status", "DONE")
.whereNotIn("uniqueId", idList)
.orderBy("uniqueId", Query.Direction.DESCENDING)
.orderBy("createdOn", Query.Direction.DESCENDING);
/* FIREBASE DOCUMENTATION SAYS
------------------------------
Use the in operator to combine up to 10 equality (==)
clauses on the same field with a logical OR */
If the idList object have more than 10 items. It crashes the android application due to FirestoreException.
So, should we not use where_not_in operator? But I have specific demand of this for the query.
So, if you want to query using where_not_in operator in Firebase then, you have to do some part from client side also. The query has a serious limitation. So here is a solution.
// Assume idList contains the uniqueId of documents that you don't want
// Assume status can be DONE or PENDING
// Assume list_objects is the ArrayList you have to pass to Recycler view or list view in your app
if (idList.size() > 0 && idList.size() <= 10) {
query = db.collection("my_collection")
.whereEqualTo("status", "DONE")
.whereNotIn("uniqueId", idList)
.orderBy("uniqueId", Query.Direction.DESCENDING)
.orderBy("createdOn", Query.Direction.DESCENDING);
// your on success code here
} else {
query = db.collection("my_collection")
.whereEqualTo("status", "DONE")
.orderBy("createdOn", Query.Direction.DESCENDING);
// here we are fetching all data where status is done
query.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
List<Object> list_toRemove = new ArrayList<>();
list_objects = task.getResult().toObjects(ClassName.class);
for (int i = 0; i < list_objects.size(); i++) {
Object item = list_objects.get(i);
if (idList.contains(item.getUniqueId())) {
list_toRemove.add(list_objects.get(i));
}
}
list_objects.removeAll(list_toRemove);
}
}
// remove the data manually here and we are now good. There is no other way for now.
}
So this is a limitation in firebase, but if we look at the advantages of using firebase firestore DB then it's a trade off.
The rule is that if you cannot filter data using query then fetch with applying filters that are possible then again filter using the Collection Framework (if you are using Java). All things are possible in this DB.
I want to calculate how many documents are in a collection, not the length of the document. I have tried it with some code but what appears is the length of the character from my document name.
this my code :
StreamSubscription<DocumentSnapshot> userpost;
final DocumentReference documentReference =
Firestore.instance.document("product/$documentPost");
userpost = documentReference.snapshots().listen((datasnapshot) {
if (datasnapshot.exists) {
for (int i = 0; i < datasnapshot.data.length; i++){
print(datasnapshot.data.length);
}
An Example Function to fetch Documents Count.
void countDocuments() async {
QuerySnapshot _myDoc = await Firestore.instance.collection('product').getDocuments();
List<DocumentSnapshot> _myDocCount = _myDoc.documents;
print(_myDocCount.length); // Count of Documents in Collection
}
You can use the count() function which was added in cloud_firestore version 4.0.0
Accepted answer might be a bad solution because you have to fetch all the documents just to count the number of documents. As per Firestore pricing, every document read is taken as 1 read count.
So a better solution is to use the count() function instead.
AggregateQuerySnapshot query = FirebaseFirestore.instance.collection('random_collection').count().get();
int numberOfDocuments = query.count;
count() is an Aggregation Query
PS: You might need to update your firebase plugins in pubspec.yaml.
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>> userList = FirebaseFirestore.instance.collection('users');
Future<int> countUsers() async {
AggregateQuerySnapshot query = await userList.count().get();
debugPrint('The number of users: ${query.count}');
return query.count;
}
In my firestore database,there are 12+ documents.I am getting the first 3 documents correctly by calling the below function on button click. But on the secondclick, though the documentReference is passed correctly, its not retrieving any data.The querySnapshot size is coming 0. What could be the problem.
Given below is the declaration
private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
private val colRef: CollectionReference = db.collection("Notebook")
private var lastResult: DocumentReference? = null
private lateinit var query: Query
and below is the onButtonClick code :
private fun loadNoteNew() {
#Suppress("SENSELESS_COMPARISON", "LiftReturnOrAssignment")
if (lastResult == null) {
query = colRef.orderBy("priority")
.limit(3)
} else {
Log.i(TAG, "Start ${lastResult!!.id}")
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
}
Log.i(TAG, "before get")
query.get()
.addOnSuccessListener { querySnapshot ->
var data = ""
Log.i(TAG, "querySnapshot Size : ${querySnapshot.size()}")
if (lastResult != null) {
Log.i(TAG, "querySnapshot ID : ${lastResult!!.id}")
}
for (snapshot in querySnapshot) {
val note = snapshot.toObject(Note::class.java)
note.id = snapshot.id
val title = note.title
val desc = note.description
val priority = note.priority
data += "${note.id} \nTitle =$title \nDescription = $desc\nPriority : $priority\n\n"
}
if (querySnapshot.size() > 0) {
data += "---------------\n\n"
textView_loadData.append(data)
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
Log.i(TAG, lastResult!!.id)
}
}
}
Given below is the logcat for first click
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 3
I/FireStoreExample: P9hIw4Ai7w4IHP6H3ew3
and given below is the logcat of second click
I/FireStoreExample: Start P9hIw4Ai7w4IHP6H3ew3
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 0
I/FireStoreExample: querySnapshot ID : P9hIw4Ai7w4IHP6H3ew3
Please help me find out,where i am getting it wrong.
Thanks
The second query result is empty because of a misunderstanding on the semantics of query pagination using startAt and startAfter methods.
Let's say the Notebook collection contains N documents. When you make the first query you're asking for the first 3 documents ordered by the priority field so the query is returning documents 1..3. Then upon the second click you're expecting the query to return the next 3 results so indeed you're expecting documents 4..6. The keypoint here is that both startAt and startAfter paginate based on the value of the ordered field rather than with the last document retrieved. Overall the semantics of startAt and startAfter are roughly as follows.
orderby(X).startAt(Y) => Return documents whose X field is greater than or equal Y
orderby(X).startAfter(Y) => Return documents whose X field is strictly greater than Y
With that in mind, let's examine what the code is actually doing when you make the second query:
// At the end of the first query...
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
// Second query
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
In the code above you're asking for the documents whose "priority" field is greater than document reference "P9hIw4Ai7w4IHP6H3ew3" and indeed there are no documents greater than that, therefore the result set is empty. Here is api reference for both.
There is yet another thing to note. Because these methods filter upon the fields value the position of the cursor could be ambiguous. For instance, if you have 4 documents with priority 3 and already retrieved the leading three if you set startAfter(3) you'll be missing a document. Similarly, if startAt(3) were to be made you'll get back the same three documents. This is also pointed out in the documentation. All in all you have a couple of options to make this work as intended:
Add another orderby in another field so that documents are uniquely identified by the combination so to prevent any cursor ambiguity and be able to use startAfter with guarantees. Next snippet build upon the doc samples and your code.
// first query
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.limit(3)
// Save last document
lastResult = querySnapshot.documents[querySnapshot.size() - 1]
// Second and next queries
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.startAfter(lastResult)
.limit(3)
Lastly remember that it might be simpler to just query all the documents if they're not many and delay optimizations until they become a performance issue.
I am trying to create a query which only selects documents whose reference is equal to a given reference, using Java for Android development. A document which it would match contains the reference for the path "/users/someUser". I am creating the reference like so:
DocumentReference ref = mDatabase.document("users/someUser");
I have also tried:
DocumentReference ref = mDatabase.document("/users/someUser");
Then the query:
Query query = mDatabase.collection("myCollection").whereEqualTo("refField", ref).limit(10);
However, when I run the query and check the task.isSuccessful() in the onComplete method, it's not passing, i.e. it didn't work, whereas when I remove the .whereEqualTo(), it passes and the task's result isn't empty. How can I properly use .whereEqualTo() to check for all documents containing a specific reference?
An example of a document that should match my query would be:
/myCollection/GDpojS5koac2C7YlIqxS which contains the field:
refField: /users/someUser (value of type reference)
And an example of a document that should not match my query would be:
/myCollection/J5ZcVAMYU1nI5XZmh6Bv which contains the field:
refField: /users/wrongUser (value of type reference)
I think you need to add a get() method to run the query and add an onCompletionListener.
Something like this should work:
mDatabase.collection("myCollection")
.whereEqualTo("refField", ref)
.limit(10)
.get()
.addOnCompleteListener({task ->
if(task.isSuccessful){
val result = task.result
})
The above example is in kotlin, but i guess in java it is something similar
You need not to worry about the documents, if you create a query based on your fields then all the documents will be returned in the "QuerySnapshot" object,
for eg,
CollectionReference collectionReference = db.collection(FIRESTORE_USERS);
DocumentReference documentReference = collectionReference.document(userID);
CollectionReference notificationCollection = documentReference.collection(FIRESTORE_NOTIFICATIONS);
notificationCollection.whereEqualTo(USER_TYPE, userType)
.whereGreaterThanOrEqualTo(SEND_AT, calendar.getTime())
.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
List<DocumentSnapshot> snapshotsList = documentSnapshots.getDocuments();
ArrayList<NotificationCollections> notificationCollectionsArrayList = new ArrayList<>();
for (DocumentSnapshot snapshot : snapshotsList) {
// each document having that particular field based on query
}
}});
in the above example I am fetching all those documents which match a particular user id and also having time greater than or equal to supplied time (time will not be used in your case)
I hope this helps...
Happy coding :)
I'm trying to list documents that matches field String value from ArrayList.
Simply:
I have ArrayList with tags stored at runtime
and documents with field tag
and I want to query documents that matches tag with one of tags stored in ArrayList. Is this possible with official query or does I have to download all documents and filter it client-side? Thanks for any answers.
Also, this is my method generating query:
public static Query getQueryForFollowed(DocumentSnapshot snapshots) {
if (snapshots == null || !snapshots.exists()) {
return FirebaseFirestore.getInstance().collection("posts").whereEqualTo("null", "null"); // return query that will get nothing
}
ArrayList<String> f = processFollowedTags(snapshots);
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0));
for (int i = 1; i < f.size(); i++) {
query = query.whereEqualTo("tag", f.get(i));
}
return query;
}
I have debugged code and query has contained requested conditions, but query didn't found any document matching it.
Try This
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0)).orderBy("tag", Query.Direction.ASCENDING);;
After some more search on Google I have found that querying field to multiple values is not available.
According to:
https://stackoverflow.com/a/46633294/8428193
https://github.com/firebase/firebase-js-sdk/issues/321
Below code snippet may help you.
fun arrayContainsQueries() {
// [START array_contains_filter]
val citiesRef = db.collection("cities")
citiesRef.whereArrayContains("regions", "west_coast")
// [END array_contains_filter]
}
ref : git
As of Nov 2019 this is now possible to do with the in query.
With the in query, you can query a specific field for multiple values
(up to 10) in a single query. You do this by passing a list containing
all the values you want to search for, and Cloud Firestore will match
any document whose field equals one of those values.
it would look like this:
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereIn("tag", f);