0

I want to read collection on basis of multiple array item.

db.collection("questionCollection")
                .orderBy("questionID", Query.Direction.DESCENDING)
                .whereArrayContains("tags","EveryDayScience")
                //.whereArrayContains("tags","generalKnowledge")//this cannot be possible
                .get()
                .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                    @Override
                    public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                        if (queryDocumentSnapshots.isEmpty()) {
                            Log.d(TAG, "onSuccess: LIST EMPTY");
                            return;
                        } else {
                            // Convert the whole Query Snapshot to a list
                            // of objects directly! No need to fetch each
                            // document.
                            questionList = queryDocumentSnapshots.toObjects(QuestionBO.class);
                        }
                    }
                }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                e.printStackTrace();
                Toast.makeText(mContext,"Failed",Toast.LENGTH_LONG).show();
            }
        });

I need to read all question which belongs to everyDayScience and generalKnowledge or multiple tags like physics, chemistry etc.

My app architecture is given below. How could I structured my db to read collection on multiple tags?

enter image description here

Edit I require pagination in question, like ,favourite and comment question. and you have recommend me This structure

In MongoDb it is achieved like this

Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300

1 Answers1

1

If you try to chain multiple whereArrayContains() methods, you're most likely getting the following error:

Caused by: java.lang.IllegalArgumentException: Invalid Query. Queries only support having a single array-contains filter.

So unfortunately Firestore can only allow a single call to whereArrayContains() method. In this case, you should consider augmenting your database structure to allow a reverse lookup by adding under each tag object (document) from your tagCollection, a new collection named tagQuestion in which you should add all the questions that are labeled with a specific tag. Your database structure should look like this:

Firestore-root
  |
  --- tagCollection (collection)
         |
         --- EveryDayScience (document)
         |    |
         |    --- tagQuestions (collection)
         |          |
         |          --- tagQuestionId
         |                 |
         |                 --- //question details
         |
         --- generalKnowledge (document)
              |
              --- tagQuestions (collection)
                    |
                    --- tagQuestionId
                           |
                           --- //question details

To get all the questions from two specific tags, please use the following lines of code:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query firstQuery = rootRef.collection("tagCollection").document("EveryDayScience").collection("tagQuestions");
Query secondQuery = rootRef.collection("tagCollection").document("generalKnowledge").collection("tagQuestions");

Task firstTask = firstQuery.get();
Task secondTask = secondQuery.get();

Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
    @Override
    public void onSuccess(List<Object> list) {
         //Do what you need to do with the list of questions from within two tags
    }
});

But you'll be thinking, why to do this? Why to duplicate data? Well, there is no problem with duplicating data, when it comes to Firebase. This is a quite common practice, which is named denormalization and for that, I recommend you see this video, Denormalization is normal with the Firebase Database. This is for Realtime Database but same principles apply to Cloud Firestore.

When you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.

Asking Questions
  • 404
  • 8
  • 16
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Yes with the details that you provided me at that time, yes, I gave the best option I could think of but now knowing that you need to query the database after multiple tags, this is a good structure that I recommend it. Have you tried to use it? – Alex Mamo Sep 03 '18 at 12:49
  • User may want filter data on two or more subjects like math,physics,chemistry. And for the first time When no subject tag is selected I have to show most like recent question . Also want to show the list of favourite questions of a user. Does this structure fill all the requirement. – Zar E Ahmer Sep 04 '18 at 04:31
  • Yes it does. If you want to filter on two or more subjects, the above will work perfectly fine but instead of two tasks you can pass to the `whenAllSuccess()` method three tasks or even a `List`. If there is no tag set to a question then just query the question node according to the date and use also a limit of 10, let's say last 10 questions. If you want to have a favorite list, then you should create another collection in which you should add all questions that were marked as a favorite. So this schema will indeed help achieve what you want. Can I help you with other informations? – Alex Mamo Sep 04 '18 at 06:44
  • First of all when I use whenAllSuccess() for two tags like everyDayScience and generalKnowledge. It will fetch question twice which have both tag everyDayScience and generalKnowledge. And secondly when no tag is selected how could I iterate the question which are under different tagCollection . – Zar E Ahmer Sep 04 '18 at 07:47
  • If it will fetch question twice, then you should remove the duplicates by checking the tag name, if this is what you need. When no tag is selected, create another query that searches question directly in the questions collection, right? – Alex Mamo Sep 04 '18 at 08:07