1

My collection here

My data class

data class Data(
    val Name: String,
    val Numbers: List<String>,
    val Doctors: Map<String, Map<String, List<String>>>
)

But this is not working as I am not able to deserialize the returned snapshot,

I think the problem lies in the Doctors node which contains a map with String keys 1 and 2, they are maps as well and contain a Name which stores a name as a string and Numbers which contain numbers as an arraylist.

what will be the proper data class and how do I query the specialization of the Doctors?? If that's not possible in firestore how do I go about doing it?

Pemba Tamang
  • 1,235
  • 16
  • 38
  • Is there any way you can change the document structure? – Alex Mamo May 17 '21 at 09:02
  • yes I am open to it. Ultimately I want to query for the specialization array as efficiently as possible so I decided to cram everything inside a single collection. Is that even possible ? How would you structure your data given the said requirement. – Pemba Tamang May 17 '21 at 09:30
  • Ok, I'll write you an answer right away. – Alex Mamo May 17 '21 at 09:34

1 Answers1

2

If your app will be widely used, nesting lots of "doctors" within a single document, might not be the best solution. The best scalable way to store a large list of data in Firestore is using documents in a collection. It's true that an array is a supported data-type, but this type of field doesn't scale for growing lists. There is also another issue, the documents in Firestore have limits. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:

Maximum size for a document: 1 MiB (1,048,576 bytes)

As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text, you can store pretty much but as your array gets bigger, you might exceed this limitation, which will obviously cause problems at scale. This approach is also generally considered a bad design.

So a viable schema for your data should look like this:

Firestore-root
   |
   --- doctors
         |
         --- $doctorId
                |
                --- name: "Doctor Name"
                |
                --- phoneNumbers: ["+91...", "+91..."]
                |
                --- specialization: ["cardiology", "dermatology"]

As you can see, both" phoneNumbers" and "specialization" properties are arrays. Now it's recommended to store data in arrays as each one of them will hold a small amount of data.

Ultimately I want to query for the specialization array as efficiently as possible

To get all the doctors with a specific specialization, for example "cardiology", please use the following lines of code:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference doctorsRef = rootRef.collection("doctors");
doctorsRef.whereArrayContains("doctors", "cardiology").addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            for (QueryDocumentSnapshot document : task.getResult()) {
                Log.d("TAG", document.getString("name"));
            }
        } else {
            Log.d(TAG, "Error getting documents: ", task.getException());
        }
    }
});

The result in the logcat will be:

Doctor Name

P.S. Always consider storing the name of the fields in your class to match the ones in the database.

Edit:

Would it be possible to query the specialization and days together in a single collection?

If the days will also be an array, no you cannot because there is another limitation:

You can use at most one array-contains clause per query. You can't combine array-contains with array-contains-any.

If you want to query according to multiple values, then you should consider not using arrays at all, but simple properties:

Firestore-root
   |
   --- doctors
         |
         --- $doctorId
                |
                --- name: "Doctor Name"
                |
                --- phoneNumbers: ["+91...", "+91..."]
                |
                --- specializationOne: "cardiology"
                |
                --- specializationTwo: "dermatology"
                |
                --- dayOne: "Monday"

Then use a query that looks like this:

doctorsRef.whereEqualTo("specializationOne", "cardiology").whereEqualTo("dayOne", "Monday").addOnCompleteListener(/* ... /*);
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • The root collection here is actually a clinic which is storing the details of a clinic. The fields `Name` and `Numbers` at the bottom are meant to keep the clinic name and contact numbers. Therefore the number of doctors will be 2 to 3 at most. Also there is another array called `days` which I haven't added as of yet which will hold the days on which the doctors are available in an array. Would it be possible to query the specialization and days together in single collection? – Pemba Tamang May 17 '21 at 10:16
  • Or do I have to break up the data. Get the available doctors based on the specialization and day then get the clinic where the doctor id exists ?? Btw I can't thank you enough for your detailed answer. I just need to clarify the above points before I move ahead. – Pemba Tamang May 17 '21 at 10:21
  • In that case, please check my edited answer. – Alex Mamo May 17 '21 at 10:27
  • thank you @AlexMamo, and one last thing? Is there a way to generate kotlin data classes based on the collection data ? – Pemba Tamang May 17 '21 at 10:38
  • No, you need to create those classes yourself. There is no API for doing that. – Alex Mamo May 17 '21 at 10:39