iterate all data in firestore - java

I am making an android app to average all rating data in Firestore. This is the structure of my Firestore. I want to retrieve all rating_num which are [1, 4, 5] using DocumentSnapshot
reviews
|-my_document1
|- rating_num:1
|-my_document2
|- rating_num:4
|-my_document3
|- rating_num:5
This is what I have done so far:
Query query = FirebaseFirestore.getInstance().collection("reviews").whereEqualTo(rating_num,??) //no clue
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
Log.d("debug", "print all rating number using for loop");
for (QueryDocumentSnapshot document : task.getResult()) {
String cum_rating = document.getString("avg_quality");
Log.d("debug", cum_rating);
}
}
});
I have no clue how to match the only rating_num in all of my documents, and it does not show anything in my for loop, any help would appreciate.
Please also note that there is no need to call any .whereEqualTo(), as you don't need to match any property to a specific value.

To get the value of rating_num property from all documents and create an average, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference reviewsRef = rootRef.collection("reviews");
reviewsRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
long count = 0;
for (QueryDocumentSnapshot document : task.getResult()) {
count =+ document.getLong("rating_num");
}
long average = count / task.getResult().getDocuments().size();
Log.d("TAG", "avarage: " + average);
}
}
});
If you only have three documents with those three values, 1, 4, and 5, the result in the logcat will be:
3.33

Related

Query not going deep enough?

I am using Firestore & Java.
I am trying to change multiple values by query and batch.
My DB
Kullanıcılar
-deneme#deneme.com
--Hayvanlar
---RandomId1
"ozel", "E65" //I WANNA UPDATE THIS (First))
---RandomId2
----Dogru
-----RandomId
------Acep
"ozel", "E65" //AND THIS (Second)
My Code
CollectionReference cr = fs.collection("Kullanıcılar/" + deneme#deneme.com + "/Hayvanlar");
Query query = cr.whereEqualTo("ozel", "E65");
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = fs.batch();
for (DocumentSnapshot ds : task.getResult()) {
batch.update(ds.getReference(), "ozel", "E75");
}
batch.commit();
}
}
});
When i run this codes my only first value updates.
I never reach to second value :/
Move batch.commit() outside your for loop

How do i List out Firebase Document wihtin a Collection?

I have a collection called 'Quiz' and its document contains Quiz category. How do I get all the category documents? Like only(Science,Technology..etc..)
View Image
I have tried with this code:
db.collection("Quiz")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d("KKKK : ", document.getId() + " => " + document.getData());
}
} else {
Log.d("KKKK : ", "Error getting documents: ", task.getException());
}
}
});
But it never returns a value I have changed Firebase rule to allow read, write: if true;
If db object is defined as follows:
FirebaseFirestore db = FirebaseFirestore.getInstance();
To get all documents that exist within Questions subcollection, please use the following reference:
db.collection("Quiz").document("Science")
.collection("Questions")
.get()
.addOnCompleteListener(/* ... */);
See, you need to add all collection and document names in your reference, not only one, as it is in your actual code right now.

How to get data from a sub-collection in Firestore on Android?

I have my Firestore database in the following way:
Image Database Firestore
I been try this to get the value of sub-collection:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
DocumentReference bulletinRef = rootRef.collection("facultades").document("3QE27w19sttNvx1sGoqR").collection("escuelas").document("0");
bulletinRef.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
Log.d(LOG_TAG, "RESULTADO DE LA CONSULTA" + "===" + document.getData());
}
}
});
But this does return null:
Image result
Please help me.
You are getting null because escuelas is not a subcollection, it is an array that holds objects of type HashMap. So the following line of code:
DocumentReference bulletinRef = rootRef.collection("facultades").document("3QE27w19sttNvx1sGoqR")
.collection("escuelas").document("0");
Will never work. If you want to get the data within the escuelas array, please note that array type fields arrive from the Cloud Firestore database as a List of maps. So please use the following lines of code:
rootRef.collection("facultades").document("3QE27w19sttNvx1sGoqR").get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<Object> list = (List<Object>) document.get("escuelas");
//Iterate throught the list and get the data
}
}
}
});
Please also note that while iterating, each element that you get from the list is of type HashMap. So you'll need to iterate again to get the corresponding data within each HashMap object.
Thanks, this really helped me, I used this code to iterate
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
rootRef.collection("facultades").document("3QE27w19sttNvx1sGoqR")
.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<Object> list = (List<Object>) document.get("escuelas");
//Iterate throught the list and get the data
Map<String, String> map = new HashMap<>();
map.put("key2", list.toString());
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
}
});
this is the result: Image result
But i feel confused, how ca I get just this:
image database
Facultades> all documents> escuelas > name

Firestore updating 2 different documents

I am using Cloud Firestore in my app and have 2 collections Customers and Properties. I have an activity where the user can update the data contained in a customer document name address etc. This code shown below is working fine.
db.collection("Customers").document(customer.getCustomerId())
.update(
"name", c.getName(),
"email", c.getEmail(),
"phoneNo", c.getPhoneNo(),
"address", c.getAddress(),
"creatorId", c.getCreatorId()
)
.addOnCompleteListener(new OnCompleteListener<Void>() {
I have the document reference of the customers saved in the Properties documents so I can reference which customer owns which property. Using this reference I want to search for Properties containing that reference and update the name field if it has been changed. I have tried adding the code into my method after the onComplete checks for the above code, but it doesn't update the name field every time only every few attempts.
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propRef = rootRef.collection("Properties");
propRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
Map<Object, String> map = new HashMap<>();
map.put("customer", customerName);
propRef.document(document.getId()).set(map, SetOptions.merge()).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Is there a way to achieve what I am trying to do?
I did think I could do it using batch but from what I have read this does not allow searching.
#AlexMamo
This is a document from my Customers collection
This is a linked document from my Properties collection
Customers Structure
Properties Structure
According to your comments, to solve your the issue, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propertiesRef = rootRef.collection("Properties");
CollectionReference customersRef = rootRef.collection("Customers");
customersRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String customerName = document.getString("name");
propertiesRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot doc : task.getResult()) {
propertiesRef.document(doc.getId()).update("customer", customerName);
}
}
}
});
}
}
}
});
See you should use different CollectionReference objects, propertiesRef and customersRef, you are using a single one.
you need to implement the Interface from firestore OnDataChange and receive the new values of your variables bind to your views.
in that case when you modify or update a value in your firestore the change triggers this interface and you can assign the new change.
ValueEventListener myListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Customer customer = dataSnapshot.getValue(Customer.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
rootRef.addValueEventListener(myListener);
hopefully it works for you.
https://firebase.google.com/docs/database/android/read-and-write?authuser=0

Firestore not querying second collection

I'm kind of new to android studio and firestore database and
I'm having some trouble with querying my second firestore collection. As the title says, i am querying two collections, first one is:
with the code :
firestore = FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.build();
firestore.setFirestoreSettings(settings);
firestore.collection("Obiective").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
//<--------- Check if firestore entry is already downloaded into file --------->
SingletonObjectivesId.getInstance().getIds().clear();
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, task.getResult().size() + " number of documents");
SingletonObjectivesId.getInstance().setSize(task.getResult().size());
if(document.exists() && document != null) { ...
and the second collection have the following format:
with the code:
firestore.collection("Routes")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, task.getResult().size() + " = task.getResult().size()");
for (QueryDocumentSnapshot document : task.getResult()) {
objectives_id.clear();
id_route = document.getId();
if(document.exists() && document != null) {
Map<String, Object> map = document.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String field_name = entry.getKey() + "";
String id = document.getString(field_name) + "";
objectives_id.add(id);
}
}
routes.add(new Route(objectives, objectives_id, id_route));
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
As you can see in the second code i added a Log.d ( after if (task.isSuccessful()) ) who will display the number of documents. In my case, the first query Log.d returns 3 and the second returns 0 despite the fact that i have 2 documents in there. How can i access this 2 documents ?
Thank you.
Firebase APIs are asynchronous, meaning that the onComplete() method returns immediately after it's invoked, and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the number of documents that you try to log, is not populated from the callback yet.
Basically, you're trying to use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to move the code that queries the second collection inside the first callback (inside the onComplete() method) so-called nested queries, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
After i followed the steps from the video, i updated the code like this:
I have a global variable firestore created at the beginning of my class
private FirebaseFirestore firestore;
I have two methods readDataObjective and readDataRoute and two interfaces FirestoreCallback and FirestoreCallbackRoutes
readDataRoutes
private void readDataRoute(FirestoreCallbackRoute firestoreCallbackRoute){
firestore.collection("Trasee").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) { ...
readDataObjective
private void readDataObjective(FirestoreCallback firestoreCallback){
firestore.collection("Obiective").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
SingletonObjectivesId.getInstance().getIds().clear();
for (QueryDocumentSnapshot document : task.getResult()) { ...
Interfaces
private interface FirestoreCallback{
void onCallback(ArrayList<Objective> list);
}
private interface FirestoreCallbackRoute{
void onCallback(ArrayList<Route> list);
}
And in onCreate method i call readDataObjective and readDataRoute like this
firestore = FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().build();
firestore.setFirestoreSettings(settings);
readDataObjective(new FirestoreCallback() {
#Override
public void onCallback(ArrayList<Objective> list) {
for(Objective item : list){
//Create plainText Object - delimiter "/~/"
String data = "Title:" + item.getTitle() + "/~/" +
............................
} else if(str.contains("Longitude:")){
obj.setLongitude(str.substring(10,str.length()));
}
start = crt + 2;
}
}
SingletonObjectivesArray.getInstance().getObjectives().add(obj);
}
readDataRoute(new FirestoreCallbackRoute() {
#Override
public void onCallback(ArrayList<Route> list) {
Log.d(TAG, 2 + " ");
ArrayList<Objective> routeObjectives = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
routeObjectives.clear();
for (int j = 0; j < SingletonObjectivesArray.getInstance().getObjectives().size(); j++){ ...
With the mention that readDataRoute is called inside readDataObjective, at the end of it.
I noticed that the problem is not only with the second query, but with the first one too. I added a new document into the first collection and after running the code, the first query return the old data ( without my new entry ).

Categories