Home > Net >  Firestore query doesn't return results sometimes when in a loop
Firestore query doesn't return results sometimes when in a loop

Time:09-27

I am facing an issue here in my code. I am trying to get a collection in Firestore but sometimes the query returns a result and sometimes it doesn't.

Solutions I have applied

  1. Turned off the persistence

  2. Querying data through .get(Source. SERVER)

     for (int i = 1; i <= 31; i  ) {
       String date = String.format("d", i)   "-"   String.format("d", currentMonth)   "-"   2022;
    
       int finalI = i;
       monthRef.document(date)
               .collection("AttendanceSections")
               .document(student.getCurrentSection())
               .collection("AttendanceStudents")
               .whereEqualTo("StudentID", student.getStudentId())
               .get(Source.SERVER)
               .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                   @Override
                   public void onComplete(@NonNull Task<QuerySnapshot> task) {
                       if (task.isSuccessful()) {
                           if (task.getResult().size() > 0) {
                               for (QueryDocumentSnapshot document : task.getResult()) {
                                   StudentAttendance studentAttendance = document.toObject(StudentAttendance.class);
                                   studentAttendance.setDate(date);
                                   attendanceList.add(studentAttendance);
                               }
                           }
                       } else {
                           Log.d(TAG, "onComplete: "   task.getException().getMessage());
                       }
    
                       if (finalI == 31) {
                           Log.d(TAG, "onComplete: "   attendanceList.size());
                           progressBar.setVisibility(View.INVISIBLE);
                           if (attendanceList.size() > 0) {
                               drawDecorators();
                           }
                       }
    
                   }
               }).addOnFailureListener(new OnFailureListener() {
           @Override
           public void onFailure(@NonNull Exception e) {
           }
       });
    

    }

Point to note here is the date that I passing in the loop.

String date = String.format("d", i) "-" String.format("d", currentMonth) "-" 2022;

On 20-09-2022 a collection exists but sometimes it returns results in the task and sometimes it doesn't. I am unable to figure out the solution.

Firestore Structure 1 enter image description here

Any help would be appreciated.

CodePudding user response:

With your current code, the order that the queries complete in is not guaranteed. If your query for day 31 doesn't have any results (like it would for certain months of the year), it is likely to finish before any of the other days causing your list to be rendered out before it's properly filled.

You need to correct this by correctly waiting for them all to complete first and then hiding the progress bar.

By using some java.util.Stream APIs, you gain the flexibility to arbitrarily apply filters to your collection query, and then fetch and collate all matches into a single list of StudentAttendance instances, while maintaining the correct order.

// create a list to hold all the fetch tasks
List<Task<Stream<StudentAttendance>>> fetchStudentsTasks = new ArrayList<Task<Stream<StudentAttendance>>>(31);

for (int i = 0; i <= 31; i  ) {
  // for each computed date, fetch the students who attended on those dates
  String date = String.format("d", i)   "-"   String.format("d", currentMonth)   "-"   2022;
  fetchStudentsTasks.add(
    monthRef.document(date)
            .collection("AttendanceSections")
            .document(student.getCurrentSection())
            .collection("AttendanceStudents")
            .whereEqualTo("StudentID", student.getStudentId()) // omitting this line would get all students in attendance
            .get(Source.SERVER)
            .continueWith(t -> { // continueWith manipulates completed tasks, returning a new task with the given result
              // The t.getResult() below will throw an error if the get
              // documents task failed, effectively rethrowing it, which
              // is OK here. If you want to ignore any failed queries,
              // return Stream.empty() when t.isSuccessful() is false.
              return StreamSupport
                .stream(t.getResult().spliterator(), false) // streams the documents in the snapshot so we can iterate them
                .map(document -> { // take each document and convert it to a StudentAttendance instance, with the right date
                  StudentAttendance studentAttendance = document.toObject(StudentAttendance.class);
                  studentAttendance.setDate(date);
                  return studentAttendance;
                }) // this returned Stream is 'open', waiting for further processing
            })
  );
}

Tasks.whenAll(fetchStudentsTasks)
  .addOnSuccessListener(() -> {
    // if here, all queries returned successfully, update the list
    attendanceList = fetchStudentsTasks
      .stream()
      .flatMap(t -> t.getResult()) // pull out the streams of StudentAttendance instances and merge them into one stream (closing each child stream when finished)
      .collect(Collectors.toList()) // join all the StudentAttendance instances into one large ordered list

    Log.d(TAG, "onComplete: "   attendanceList.size());
    progressBar.setVisibility(View.INVISIBLE);
    if (attendanceList.size() > 0) {
      drawDecorators();
    }
  })
  .addOnFailureListener(() -> {
    // if here, one or more queries failed
    // todo: iterate over fetchStudentsTasks to pull out the exceptions
    Log.d(TAG, "One or more fetch students tasks failed.");
  });
  • Related