0

My Firestore Database structure looks like this:

  1. ...a Collection with Routine Objects.

  2. ...a Collection with Workout Objects. With the attributes

    -> RoutineKey: Stores the Key of the Routine which the Workout is from

    -> ExerciseEntryKeys: ArrayList<String> of the Keys of the ExerciseEntry from the Workout

  3. ...a Collection with ExerciseEntries Objects.

Now I want to load every Workout from a Routine and the ExerciseEntries of a Workout. To do this, I do the following after I have loaded a Routine Object.

for (final DocumentSnapshot doc : documentSnapshots.getDocuments()) {

                    final WorkoutSNR workout = doc.toObject(WorkoutSNR.class);
                    workout.setKey(doc.getId());

                    workoutsFromRoutine.add(workout);

                    fm.getColRefExerciseEntries().whereEqualTo("workoutKey", workout.getKey()).get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                        @Override
                        public void onSuccess(QuerySnapshot documentSnapshots) {
                            if (documentSnapshots.isEmpty()) {
                                prg.setVisibility(View.GONE);
                                processData();
                            } else {

                                for (int i = 0; i < workout.getExcersiseEntryKeys().size(); i++) {

                                    fm.getDocRefExerciseEntrie(workout.getExcersiseEntryKeys().get(i)).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                                        @Override
                                        public void onSuccess(DocumentSnapshot documentSnapshot) {

                                            final ExcersiseEntrySNR entry = documentSnapshot.toObject(ExcersiseEntrySNR.class);
                                            entry.setKey(documentSnapshot.getId());

                                            workout.getExcersises().add(entry);

                                            processData();
                                            prg.setVisibility(View.GONE);

                                            Collections.sort(workout.getExcersises(), new Comparator<ExcersiseEntrySNR>() {
                                                @Override
                                                public int compare(ExcersiseEntrySNR e1, ExcersiseEntrySNR e2) {
                                                    if (e1.getPosition() < e2.getPosition()) {
                                                        return -1;
                                                    } else if (e1.getPosition() > e2.getPosition()) {
                                                        return 1;
                                                    } else {
                                                        return 0;
                                                    }
                                                }
                                            });
                                        }
                                    });
                                }
                            }
                        }
                    });
                }
            }
        });

This works like it should but as you can see I call:

processData();
    prg.setVisibility(View.GONE);

    Collections.sort(workout.getExcersises(), new Comparator<ExcersiseEntrySNR>() {
        @Override
        public int compare(ExcersiseEntrySNR e1, ExcersiseEntrySNR e2) {
            if (e1.getPosition() < e2.getPosition()) {
                return -1;
            } else if (e1.getPosition() > e2.getPosition()) {
                return 1;
            } else {
                return 0;
            }
        }
    });

Evertime an ExerciseEntry has been successfully loaded. This is very unnecessary and I want to call this code only once everything(Every ExerciseEnry for every Workout of an Routine).

What is the best way to notice everything has been loaded? Does Firestore provide any function for this?

I have tried having an Integer that counts the number of successful ExerciseLoads and Workout loads but I can only access final variables inside a nested class(Is that how its called?).

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Jonas
  • 7,089
  • 15
  • 49
  • 110

2 Answers2

0

How do I know when the data is completely loaded from the database?

You can add a flag to each Routine and Workout objects with the value of false and once you have downloaded those objects, to set the value to true but this is not how things are working with Firestore. You cannot know when an object from the database is completed downloaded becase Cloud Firestore is also a realtime database and getting data might never complete. That's why is named a realtime database because in any momemnt the data under those Routine and Workout objects can be changed, properties can be added or deleted.

You can use a CompletionListener only when you write or update data and you'll be notified when the operation has been acknowledged by the Database servers but you cannot use this interface when reading data.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • So is there nothing I can do there? I mean sorting my ArrayList every time an Objects has been loaded can't be the way to go. – Jonas Apr 24 '18 at 12:34
  • Sorting an ArrayList every time an object has been loaded doesn't mean that you have finished loading the data from the database. Because in every moment you database can be changed, you can never say that your loading is completed. But this is not a deficiency, this is how a realtime database works. – Alex Mamo Apr 24 '18 at 12:42
  • But I'm not using any ChangeListener or anything. Im just loading the Documents and even if there are any changes on the database I don't get additional Documents. So I don´t see why I can't notice I have loaded every Document that exists at the time I´m starting to load. – Jonas Apr 24 '18 at 13:16
  • When you are using a get() call, it means that your are getting the data from the database and there is no point at all in which you can say that you have finished loading it. Please note, that either [CollectionReference](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#methods) nor [DocumentReference](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#methods) do not have a method that allow you to achieve this. And as I already mentioned in my answer, you can achieve this only when using a write operation. – Alex Mamo Apr 24 '18 at 13:52
  • Ok so Firestore does not provide me any function for this but I should be able to have an 'int count' for Workouts and Exercises and check in the onSuccess if the workout count equals the QuerySnapshot size and if the exercise count equals the ExerciseEntryKeys size, right? The problem is I can't increase an counter variable inside the onSuccess because I can only access final variables... – Jonas Apr 24 '18 at 18:59
  • No, it does not. If you want to count the number of documents within a collection, [this](https://stackoverflow.com/questions/48534676/get-collectionreference-count/48540276) is how you can do it but unfortunately I'm afraid that will not solve you the problem related to the completion of loading the data. Regarding final variables, just declare that variable to have a local scope. – Alex Mamo Apr 24 '18 at 19:20
  • you said in this thread there is no 'getDocumentCount()' method but isn't 'documentSnapshots.getDocuments().size()' (from QuerySnapshot) just that? If yes I don't understand why counting my successful loads would not work. I feel like the Realtime Database is much easier for this, maybe I should switch back to this database again even though I really like the Collection/Document concept. Thanks for your help so far, btw. – Jonas Apr 24 '18 at 19:33
  • Yes, you can count the number of documents using `task.getResult().size()` but this doesn't mean you'll know when the data is finished loading. I didn't say that counting the documents will not work, I just said that it will help you with what you want. Imagine you have 100 documents within a collection. You are running a method to count those documents but in the meanwhile, one new document is added. The result of you method is 100 but the real numebr of document is 101. So you'll end up having inconsistent data. So there is no way in which you can know when the data is finished loading. – Alex Mamo Apr 24 '18 at 19:47
  • Is there everything alright? Can I help you with other informations? – Alex Mamo Apr 25 '18 at 08:11
  • Thanks I have posed my current final solution. I might switch back to the Realtime Database because this works much better there. – Jonas Apr 25 '18 at 18:13
0

So if anyone is wondering what my Solution at the end is, here is my current Code:

fm.getColRefWorkoutSNR().whereEqualTo("routineKey", routineKey).get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot documentSnapshots) {
                final int workoutSize = documentSnapshots.getDocuments().size();
                for (final DocumentSnapshot doc : documentSnapshots.getDocuments()) {

                    final WorkoutSNR workout = doc.toObject(WorkoutSNR.class);
                    workout.setKey(doc.getId());

                    workoutsFromRoutine.add(workout);

                    fm.getColRefExerciseEntries().whereEqualTo("workoutKey", workout.getKey()).get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                        @Override
                        public void onSuccess(QuerySnapshot documentSnapshots) {
                            if (documentSnapshots.isEmpty()) {
                                prg.setVisibility(View.GONE);
                                processData();
                            } else {

                                if (workout.getExcersiseEntryKeys().size() > 0) {
                                    for (int i = 0; i < workout.getExcersiseEntryKeys().size(); i++) {

                                        fm.getDocRefExerciseEntrie(workout.getExcersiseEntryKeys().get(i)).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                                            @Override
                                            public void onSuccess(DocumentSnapshot documentSnapshot) {

                                                final ExcersiseEntrySNR entry = documentSnapshot.toObject(ExcersiseEntrySNR.class);
                                                entry.setKey(documentSnapshot.getId());

                                                workout.getExcersises().add(entry);

                                                if (workout.getExcersises().size() == workout.getExcersiseEntryKeys().size()) {
                                                    increaseFullyLoadedWorkouts();
                                                }

                                                if (fullyLoadedWorkouts == workoutSize) {
                                                    processData();
                                                    prg.setVisibility(View.GONE);
                                                }
                                            }
                                        });
                                    }
                                } else {
                                    increaseFullyLoadedWorkouts();
                                    if (fullyLoadedWorkouts == workoutSize) {
                                        processData();
                                        prg.setVisibility(View.GONE);
                                    }
                                }

                            }
                        }
                    });
                }
            }
        });

As you can see, I check if I have loaded every exercise for an workout and if thats the case I increase a "fullyLoadedWorkout" counter. Then I check if the counter equals the workout size and if thats the case I know that I have "fully" loaded my data.

I know thats not a good way to do this but its the only solution I can imagine at the moment. This seems to be way easier in the Realtime Database and I'm still consider switching back to it. Any suggestions for a better way are welcomed.

Jonas
  • 7,089
  • 15
  • 49
  • 110