9

I am trying to make a function so that users can like posts (either a post is liked by the user or it is not liked by the user, i.e. no dislike or voting).

My posts are objects in a MongoDB collection with schema

{
  title: String,
  text: String
}

while my users have schema

{
  username: String,
  password: String
}

I could, for instance, create an array at posts with the ids of the users that have liked the specific post. It would look something like

{
  title: String,
  text: String,
  likedByUsers: [ObjectID]
}

or I could create an array at users with the ids of the posts that the user has liked, which would be something like

{
  username: String,
  password: String,
  postsLiked: [ObjectID]
}

However, I expect that the users will like thousands of posts, so I might face a restriction in the size of the objects in MongoDB.

So I am thinking that I should instead create a new collection with likes

{
  userId: ObjectID,
  postId: ObjectID
}

However, I am thinking how I should retrieve it in my app.

Should I store an array of the IDs of all the posts that the user (who is logged in) has liked, and when I am printing each post, I will look if the post is among the posts that the user has liked, and then show an "already liked" button?

Alternatively, I could send the user id when I am requesting to see all the posts, and then for each post add a field saying "isLiked", representing whether an object in the Liked collection could be find, matching both the post and the user id?

styopdev
  • 2,604
  • 4
  • 34
  • 55
Jamgreen
  • 10,329
  • 29
  • 113
  • 224
  • If you actually read ["How to Model a “likes” voting system with MongoDB"](https://stackoverflow.com/questions/28006521/how-to-model-a-likes-voting-system-with-mongodb) then you will find in the content that for the "current user" and a UI perspective, you only need to know if "that user" liked the post or not. Which is basically an `$elemMatch` projection on the array. `Post.findById(id,{ "likedByUsers": "$elemMatch": { "$eq": userId } })` since all other users would be irrelevant from that users perspective. It is also redundant to also include the posts on the "User" itself. – Neil Lunn Jul 12 '17 at 02:44

1 Answers1

7

The limit of the document in MongoDB is 16mb, which is pretty huge. Unless you're trying to create the next Facebook, putting the IDs in the array won't be an issue. I have production workflows with 10k+ IDs in a single document array, have no issues. The question should not be about the document size, you have to choose where to put it based on the queries you'll need.

I would create a schema for the Post and put the Liked ids there, such as:

{
    title: { type: String },
    text: { type: String },
    likes: [{ type: ObjectId, ref: 'users' }]
}

Posts will get old, people will stop liking it, so the average size of the array if put in Post instead of the User collection should be smaller. Also, putting the ref attribute gives you the ability to use Mongoose Populate API, so you can query for posts such as:

Posts.find().populate('likes');

This will get you the full user information in the array. To search for posts that a single user has likes, is pretty simple too:

Posts.find({likes: userId}).populate('likes');

Hope it helps you.

Luís Brito
  • 1,652
  • 1
  • 17
  • 34
  • So when a user is seeing a post, I will need to do a `if (post.likes.includes(userId)) { // already liked }`? Won't it be quite heavy to do this for every single post with likes arrays of potentially thousands of IDs? – Jamgreen Jul 11 '17 at 18:15
  • Wouldn't it be better to send the user ID in the request, so I can let the database search through the likes array and only return a boolean field saying whether or not the user has liked the post? – Jamgreen Jul 11 '17 at 18:16
  • When you're handling thousand of IDs, it will always be painful to compute anything. The best way to handle it is to cache everything you can. – Luís Brito Jul 11 '17 at 18:17
  • 1
    @Jamgreen - Hi. I'm trying to accomplish something similar. How did you manage to check if the user has liked the post or not in UI? This approach doesn't feel right like you shared. `if (post.likes.includes(userId) {} ` – shivamkaushik Jul 17 '20 at 14:19
  • User should have `likedPost[]`. Imagine 1000 users and 10,000 posts. Likes a user makes will usually be much lesser than likes a good post has (which makes searching cheaper). As for post, if just showing total likes is enough, you can have a `totalLikes:Number` and `$inc` it on every like. However, if the users are very large in number, searching all user's liked array isn't efficient. In such case you can add `User[]` in post too. – helloworld Oct 16 '21 at 05:55