Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get information if the feed is liked by the user in a single query (Firestore)

Tags:

I want to show feeds for my users.
Every post has a "like" button (like in twitter, Instagram...)
I want to show a different icon if the post is already liked or not.

The problem I'm facing right now is, that I couldn't find an economical way to get the information if the post is already liked by the user. The best way would be if I could get this information with a single query. I have two ways in my mind which I think isn't the best way yet.

  1. Query the users "liked" collection, every time he loads a feed
    This would cost 2 queries per loaded post, which could be expensive in performance and billing perspective. For example, watching 20 posts would cost 40 queries.

  2. Download all liked posts for once and query offline
    I could check if the given post is already liked by the user in a client side way. But this would load many needless data at once by a user who "likes" to give likes to posts.

Am I right with my thoughts? Is there a better way to solve this problem without being too expensive for billing and performance?

like image 303
Ahmet Kazaman Avatar asked Jul 27 '19 11:07

Ahmet Kazaman


1 Answers

To solve the problem of like/unlike for your posts, as also @Hudson Hayes mentioned in his answer, I also recommend you try the following schema:

Firestore-root
   |
   --- posts (collection)
         |
         --- postId (document)
               |
               --- //Post details
               |
               --- userLikes: ["userIdOne", "userIdTwo", "userIdThree"]

As you can see, under each Post object there is an array that contains user ids. Now, everytime a user likes a post, simply add his id to the userLikes array. When the user revokes the like, simply remove his id from the array. In this case you'll be charged only with one write operation for each like/unlike.

Now to display a list of all posts and highlight which is liked and which not, simply create a list that contains posts objects. Because you know the id of the authenticated user, simply check the existence of its id in each post userLikes array. If its id exist in the array show a red heart otherwise show a grey one.

There is no need for additional queries. So IMHO, none of your solutions might apply to this use-case.

Edit:

The size of the document is indeed 1MiB but when we are talking about storing text (user ids), you can store pretty much.

If yes, this wouldn't be a solution for a scaling application.

Yes, it is a solution for scaling. 1Mib can hold 1,000,000 chars, divided to 20, the number of chars an id has, it means 50,000. If you think that your app will be so popular, so a single post will exceed the max number of 50,000 of likes, yes, it's not an option. But there is an workaround. You can create then a subcollection that will documents of likes. Each document will hold 50,000 ids. So for 150,000 likes, you'll have only 3 documents. To check that out, for every post you'll need to create an extra query to get the number of likes (in the above example three) and see if user id is in it.

Storing the likes as individual objects is not an option since for 150,000 likes you'll be charged with 150,000 read operation which is too expensive.

Furthermore it would be an additional query as well, because I couln't get the post data if the user isn't in the likers array.

You can get any data you want but remember, you don't query the users collection, you query the posts collection. This collection holds the posts of all users. If you need to see the posts of a single user, add a property inside the document named, createdBy and add the id of the user who created it. Then a query like this is required:

db.collection("posts").whereEqualTo("createdBy", uid);

To overcome this I would have to load the complete array and filter it on the client side.

There is no need for that. I explained above why.

like image 133
Alex Mamo Avatar answered Sep 28 '22 19:09

Alex Mamo