firestore query in for loop android java - java

I am new to java. I have a firestore_member_list. In firestore_member_list, it contains values: ["steve","ram","kam"]. I am using for loop to pass values one by one.
loadingbar.show()
for( int k=0; k<firestore_member_list.size();k++){
String member_name = firestore_member_list.get(k);
final DocumentReference memDataNameCol = firestoredb.collection("member_collection").document(member_name);
memDataNameCol.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
// In here, I am retreiveing the document data from firestore and assigning it to the ArrayList which is `all_mem_data`
all_mem_data.add(document.get("member_name").toString());
all_mem_data.add(document.get("member_address").toString());
Toast.makeText(getActivity(), "all mem data array"+all_mem_data.toString(),
Toast.LENGTH_LONG).show();
}
}
}
});
}
Log.d("all_mem_data",all_mem_data)
loadingbar.hide()
I know firestore executes asynchronously. Since firestore retrieves data asynchronous, before filling the array of all_mem_data, the last line gets executed and shows the empty array. How to wait for the array to get filled and after filling,execute the last two lines. Please help me with solutions.

Any code that needs the data from the database, needs to be inside the onComplete that fires when that data is available.
If you want to wait until all documents are loaded, you can for example keep a counter:
loadingbar.show()
int completeCount = 0;
for( int k=0; k<firestore_member_list.size();k++){
String member_name = firestore_member_list.get(k);
final DocumentReference memDataNameCol = firestoredb.collection("member_collection").document(member_name);
memDataNameCol.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
...
if (completeCount++ == firestore_member_list.size()-1) {
Log.d("all_mem_data",all_mem_data)
loadingbar.hide()
}
}
});
}

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

Callback Problems with Firebase and Android

I have a problem when I am fetching my Data from Firebase Firestore. I want to get multiple Documents out of my "Posts" collection and that works fine. But to get my user documents out of my "Users" collection i need to start multiple tasks(Task) that ALL need to be completed before I want to call my callback function and i cant figure out how. Is there even a way to do it with a callback? Ive tried to solve it with Continuations but had a hard time.
Thanks in advance.
Here some simple code i wrote so u can maybe understand the problem a little bit better.
public void getPosts(final postCallback callback) {
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference postsRef = db.collection("Posts");
Query postsQuery = postsRef.orderBy("createTime", Query.Direction.DESCENDING).limit(20);
// Starting the post documents
Task<QuerySnapshot> task = postsQuery.get();
task.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
QuerySnapshot querySnapshot = task.getResult();
List<DocumentSnapshot> docsList = querySnapshot.getDocuments();
for(DocumentSnapshot docSnap : docsList){
String userID = docSnap.getString("originalPoster");
// getting user documents
Task<DocumentSnapshot> userTask = db.collection("Users").document(userID).get();
userTask.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot userDoc = task.getResult();
String userID = userDoc.getId();
String firstName = userDoc.getString("first_name");
String surname = userDoc.getString("surname");
User userObject = new User(firstName, userID, surname);
// cant call my callback right here otherwise its called for every
// completed user fetch
}
});
// cant call my callback right here since its too early
}
}else if(task.isCanceled()){
System.out.println("Fetch failed!");
}
}
});
}

Wait until Firestore data is retrieved

I need after inner foreach finish and add all data in list then send to interface
but when but this line " view.setOrders(orderList); " below " orderList.add(order); "
my code run okye but not that is not performance , I need best way to collection list then send to interface ..
public ListenerRegistration getOrders() {
view.showLoading();
ListenerRegistration listenerRegistration = refOrders.addSnapshotListener((queryDocumentSnapshots, e) -> {
view.hideLoading();
if (e != null) {
view.onErrorMessage(e.getMessage());
} else {
List<Order> orderList = new ArrayList<>();
for (QueryDocumentSnapshot snapshot : queryDocumentSnapshots) {
Order order = snapshot.toObject(Order.class);
order.setOrderId(snapshot.getId());
refUsers.document(order.getPhone()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
User user = task.getResult().toObject(User.class);
order.setName(user.getName());
order.setAddress(user.getAddress());
orderList.add(order);
}
}
});
}
//Here Back List size = 0
view.setOrders(orderList);
}
});
return listenerRegistration;
}
Since get() method is asynchronous which means that the code after the CompletionListener will be executed first and then after the data is retrieved the listener will get executed.
Therefore to solve the issue add the following line view.setOrders(orderList); inside the CompletionListener
if (task.isSuccessful()) {
User user = task.getResult().toObject(User.class);
order.setName(user.getName());
order.setAddress(user.getAddress());
orderList.add(order);
view.setOrders(orderList);

Getting null string from inner function with global variable

pls help me with little issue, im sure that you can do it :D
Im trying to set up a field on a firestore document "user_cases_information" with a field "case_number"
First i declare this global var
private String case_number_consecutive = new String("");
in .setOnClickListener i have this: (to read a old consecutive or case number from other document)
if (service_type.equals("support")){
DocumentReference docRef = firestore_popup.collection("param_data_app").document("support_case_numbers");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
String consecutive = document.get("consecutive").toString();
if (consecutive.equals(null)) {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
} else {
case_number_consecutive = consecutive; UpdateCaseNumbersConsecutive("support");
}
} else {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
}
}
});
}
Map<String, Object> user_cases_information = new HashMap<>();
user_cases_information.put("case_number", case_number_consecutive);
... (then i use a firestore reference to put the new case number)
It supposed that when i check firestore, precisely the document where i set the "user_cases_information" document, i should see the number or the new case_number, but i always get a null reference, the field consecutive is always = null
Why im getting this null? i used a Toast and put it before this:
Toast.makeText(...."Value: "+case_number_consecutive
Map<String, Object> user_cases_information = new HashMap<>();
user_cases_information.put("case_number", case_number_consecutive);
and it show null so the value or data is losing inside the above functions
how can i fix this??? thank you a lot.
The reason for this, is because firebase is executing asynchronously, and you are coding as if it all happens synchronously. What does this mean ? It means that the firebase call does not immediately return a value from the server, it takes some time to get that value, but the rest of your code is executing before the response comes back, and that's why you're having problems. So, even though this code executes:
if (service_type.equals("support")){
DocumentReference docRef = firestore_popup.collection("param_data_app").document("support_case_numbers");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
String consecutive = document.get("consecutive").toString();
if (consecutive.equals(null)) {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
} else {
case_number_consecutive = consecutive; UpdateCaseNumbersConsecutive("support");
}
} else {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
}
}
});
}
by the time a response comes back, your other code :
Map<String, Object> user_cases_information = new HashMap<>();
user_cases_information.put("case_number", case_number_consecutive);
has already been completed and called, and then you never get the actual value from firebase.
have a look at my answer here : How to add results of Facebook Graph api to array list in Java and I'll help you make something similar if you need help
here's something you can do :
interface Callback{
void firebaseResponseCallback(String result);//whatever your return type is.
}
change the signature of this method, to be something like this :
public void getCaseNumber(Callback callback) {
change your code to this:
if (service_type.equals("support")){
DocumentReference docRef = firestore_popup.collection("param_data_app").document("support_case_numbers");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
String consecutive = document.get("consecutive").toString();
if (consecutive.equals(null)) {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
} else {
case_number_consecutive = consecutive; UpdateCaseNumbersConsecutive("support");
}
} else {
int random = new Random().nextInt(117) + 4;
case_number_consecutive = "IOC-09"+random;
}
callback.firebaseResponseCallback(case_number_consecutive);
}
});
}
then, when you call getCaseNumber:
getCaseNumber(new Callback() {
#Override
public void firebaseResponseCallback(String result) {
here, this result parameter that comes through is your api call result to use, so result will be your case number, so make your objects inside this method, not outside
}
});
}
Edit
I've made this post specifically for these types of questions :
How to get data from any asynchronous operation in android
There is a mistake you are doing in that code.
According to firebase latest Version, You can use the value/ String of firebase database only inside the .addOnCompleteListener/.addValueEvent/etc Function.
DocumentReference docRef = firestore_popup.collection("param_data_app").document("support_case_numbers");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
/////////////////////////////call here everything related to it////////////////////////
}
});
}
Outside the firebase add complete listener or add value listener, The variable or object value will not change or remain same!

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