2

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:

enter image description here

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:

enter image description here

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.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

2 Answers2

1

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.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thank you for the fast asnwer. I will read the links that you gave me and try to better understand how firebase works. Thank again. – Angelescu Dimitrie Feb 06 '19 at 14:10
  • Hi, i just edited my code after i watched the video and i still have the same problem. At least now the querying is made in the right order, but the second one still have no items, Log.d(TAG, task.getResult().size()) still returns 0. – Angelescu Dimitrie Feb 07 '19 at 09:16
  • I guess is might be something related to the object on which you calling the methods but without seeing your new code, I cannot be much of a help. For that, I recommend you post another fresh question using its own [MCVE](https://stackoverflow.com/help/mcve), so me and other Firebase developers can help you. – Alex Mamo Feb 07 '19 at 09:22
  • Ok, i will do this, but until then i noticed that even my first collection is not queried correctly. I updated it with one more entry (document) as a test and it seems that the new entry is not seen. May the problem be somewhere else, not in my code ? – Angelescu Dimitrie Feb 07 '19 at 09:44
  • Let's see the new question with your changed code to see what's going on there. – Alex Mamo Feb 07 '19 at 09:53
0

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 ).