I want to reduce number of reads in order to get Question's detail from
QuestionCollection
(Array of Questions) .
My app works like a part of stackoverflow.
I want to show how many likes, views and comments do a question have . So in order to handle this I created multiple collection like
QuestionCollection
CommentCollection
QuestionLikes
QuestionViews
QuestionTAG
I thought that CommentCollection (comments) only belongs to a question. So I am thinking of incorporating in QuestionCollection
like an array(s) or collection(But it requires another read hit). which one is better ?
And I think of adding two variable's(totalViews,totalLikes
) to QuestionCollection. But each time I must have to check whether this question is liked by this user or view by this user.
Give me a suggestion or an alternative so I can have the least hit to fetch question details.
Edit
For Showing View Count I iterate and count QuestionViews Collection where questionID == myQuestionID
For Showing Likes Count I iterate and count QuestionLikes Collection where questionID == myQuestionID
why I don't add it to QuestionCollection? Because I want to show most Viewed and most like question like stackoverflow. If i add it to QuestionCollection How could I know the most like and most Viewed question.
This means that Cloud Firestore will try to retrieve an up-to-date (server-retrieved) snapshot, but fall back to returning cached data if the server can't be reached. This is how we can reduce the number of reads in Cloud Firestore, by forcing the SDK to use only the offline cache and get the data only upon request.
If you leave the console open on a collection or document with busy write activity then the Firebase console will automatically read the changes that update the console's display. Most of the time this is the reason for unexpected high reads.
10 for single-document requests and query requests. 20 for multi-document reads, transactions, and batched writes. The previous limit of 10 also applies to each operation.
When you listen to the results of a query, you are charged for a read each time a document in the result set is added or updated. You are also charged for a read when a document is removed from the result set because the document has changed. (In contrast, when a document is deleted, you are not charged for a read.)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With