I want to reduce number of reads in order to get Question\'s detail from
QuestionCollection
(Array of Questions) .
The most efficient schema I can think of, is the following:
Firestore-root
|
--- questions (collections)
| |
| --- questionId (document)
| |
| --- questionId: "02cubnmO1wqyz6yKg571"
| |
| --- title: "Question Title"
| |
| --- date: August 27, 2018 at 6:16:58 PM UTC+3
| |
| --- comments (colletion)
| | |
| | --- commentId
| | |
| | ---commentId: "5aoCfqt2w8N8jtQ53R8f"
| | |
| | ---comment: "My Comment"
| | |
| | ---date: August 27, 2018 at 6:18:28 PM UTC+3
| |
| --- likes (colletion)
| | |
| | --- likeId (document)
| | |
| | --- userId: true
| |
| --- views (colletion)
| | |
| | --- viewId (document)
| | |
| | --- userId: true
| |
| --- tags ["tagId", "tagId"]
|
--- tags (collections)
|
--- tagId (document)
|
--- tagId: "yR8iLzdBdylFkSzg1k4K"
|
--- tagName: "Tag Name"
In which the comments
, likes
and views
are colletions under questionId
document. Unlike in Firebase real-time database where to display a list of question you would have been downloaded the entire question object, in Cloud Firestore this is not an issue anymore. So to avoid querying using a where
methods, you can simply get a specific question like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
DocumentReference questionIdRef = rootRef.collection("questions").document(questionId);
questionIdRef.get().addOnCompleteListener(/* ... */);
Because you can have hundreds, thousands or even more comments, likes and views to a single question, storing the data into an array will not help you. According to the official documentation:
Cloud Firestore is optimized for storing large collections of small documents.
For example, to get all the comments of a specific question, you can use the following query:
Query query = rootRef.collection("questions/"+questionId+"comments").orderBy("date", Query.Direction.DESCENDING);
Because a question can have only a few tags, you can simply use an array. To get all questions with a specific tag, you can use the following query:
Query query = rootRef.collection("questions/"+questionId+"tags").whereArrayContains("tags", tagId);
One more thing to remember, even if tags
object is stored in the database as an array, document.get("tags")
will return an ArrayList
and not an array
.
If you also want to count the number of likes or any other number of documents within a collection, please see my answer from this post.
Edit:
According to your comments:
If I want to show the most liked question of all to a user. How could I do this?
You should add the number of likes as a property inside the question object and then you can query the database according to it like this:
Query query = rootRef.collection("questions").orderBy("numberOfLikes", Query.Direction.DESCENDING);
According to this you can also store the number of likes in the Firebase real-time database and you can increase/decrease it using Firebase Transactions.
secondly if I want to show all the question related to a tag like mathematics (show all question which have mathematics tag) ??
As also mentioned above you can take atvantage of the whereArrayContains
method. You can also store the tags as String and not as id. For that, you need to change this structure:
--- tags ["tagId", "tagId"]
to
--- tags ["mathematics", "chemistry"]
And the code should look like this:
Query query = rootRef.collection("questions/"+questionId+"tags").whereArrayContains("tags", "mathematics");
the statement (userId: true) saves a lot of data repetition
Yes it does.