Sandy
Sandy11mo ago

Best practices for getting liked posts

I'm buiding an app with users and posts. Users are able to like and save posts. Based on the posts a user has liked or saved already, I want to display either a filled-in icon or a hollow icon in that post's like and save buttons. I'm wondering what would be the best way to structure the schema and create queries to make this feature easiest to implement. Here is my current schema Any advice would be hugely appreciated!
No description
24 Replies
erquhart
erquhart11mo ago
You'll want to create a join table, something like userLikes with userId and postId fields - add to the table on like, delete on unlike. When querying the list of posts to show the user, map over them and check for a userLikes entry for the current user and the given post. If the entry is present, the post was liked by the user.
Sandy
SandyOP11mo ago
Ohhh wait that makes way more sense and I'm guessing a similar table for saved posts
erquhart
erquhart11mo ago
Yep, exactly
Sandy
SandyOP11mo ago
Awesome, thank you
erquhart
erquhart11mo ago
If you index on both userId and postId, you should have 0 or 1 results as a user can only like a post once If you use .unique() instead of .collect() on the index you posted in general, you'll get either the result or null (actually I need to verify if null or undefined there) But the other feature there is unique() will throw if more than one record matches Yeah, unique returns null if no matches, that's probably what you're looking for with that query
Sandy
SandyOP11mo ago
Gotcha, I actually want to return the id of the user-post like so I can also unlike it given a user and post id. Does this seem right?
No description
erquhart
erquhart11mo ago
Yep, that's great null is a bit more idiomatic than false, but ultimately that's down to preference
Sandy
SandyOP11mo ago
Sweet, I'm gonna unmark this thread as unresolved bc I'm sure I will have more questions about this whole user likes feature as I continue building it
erquhart
erquhart11mo ago
Just fyi, what you have totally works, but you'll probably will want to pull the like status into the post query rather than having a dedicated query for getting the liked status
Sandy
SandyOP11mo ago
I see what you mean, so like add a field to post called "likedByCurrrentUser". I might just keep it this way for simplicity's sake but I might refactor later on.
erquhart
erquhart11mo ago
You're going to generally show either a single post in a dedicated view, or a list of posts. When showing a single post, you'll have a query to get the post. In that query, you would generally check to see if the current user liked it. If you're showing a list of posts, you would have a query to get the list of posts. In that query, you would get the posts from the database, and map over them to add liked status right there in the query. So you probably won't need a dedicated query for liked status. But totally, keep it moving and just keep that in the back of your mind for when you run into it.
Sandy
SandyOP11mo ago
Wait a minute, so if I pull in the like status as part of the post, when the user clicks on the like button and the client triggers a mutation and changes the like status, wouldn't that mean having to re-fetch the post? Is that something I'd have to handle by calling the query again?
erquhart
erquhart11mo ago
Nope! Convex queries are reactive, the data will update automatically.
Sandy
SandyOP11mo ago
Darn looks like vector search breaks the reactivity. I followed the same pattern as in https://github.com/JamesCowling/rap-genie/blob/main/src/App.tsx and now when I click the like button the number of likes doesnt change
GitHub
rap-genie/src/App.tsx at main · JamesCowling/rap-genie
Rap Genie is a semantic search engine for rap verses, built on Convex vector search. - JamesCowling/rap-genie
erquhart
erquhart11mo ago
You can use vector search and still have reactivity, it’s just down to implementation details. Can you share relevant code?
sshader
sshader11mo ago
You might want to check out the "movies" part of this demo https://github.com/get-convex/convex-demos/tree/main/vector-search (also mentioned here) It uses an action to load some IDs via vector search, and then a separate query to load the documents (and that part is reactive)
GitHub
convex-demos/vector-search at main · get-convex/convex-demos
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
Sandy
SandyOP11mo ago
Sure, here are screenshots of: 1. My search function "similarPosts" which is called from the client and runs vector search 2. A helper function used by search to fetch a list of posts given a list of postIds 3. The client calling similarPosts
No description
No description
No description
Sandy
SandyOP11mo ago
then I'm mapping directly over posts in my client to create cards
Sandy
SandyOP11mo ago
Is it because I'm returning "SearchResultVector" instead of a pure post object that it's not reactive?
No description
sshader
sshader11mo ago
Anything returned by an action is not reactive. If instead your client code called similarPosts, returning the result of the ctx.vectorSearch, and then after that calls something like fetchResults, the data returned by the fetchResults query would be reactive (e.g. show upvotes) even though the IDs returned by the vector search would not be. The key difference is calling the query from the client (which will be reactive) vs. calling the query within the action.
Sandy
SandyOP11mo ago
Okay one last question, react says I can't use the useQuery hook inside my handleSubmit function. Any tips on how to resolve this?
No description
No description
sshader
sshader11mo ago
Check out this: https://github.com/get-convex/convex-demos/blob/d4b6b97ce839ade881109653a6ffe8ea5ff4d082/vector-search/src/Movies.tsx#L70 In that example the useQuery is in a separate component that takes in fetchedIds as props.
GitHub
convex-demos/vector-search/src/Movies.tsx at d4b6b97ce839ade8811096...
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
sshader
sshader11mo ago
Alternatively you could do something like const postResults = useQuery(api.posts.fetchResults, fetchedIds ?? "skip") at the top of your component (https://docs.convex.dev/client/react#skipping-queries) (but personally I find that making a separate component is usually cleaner)
Convex React | Convex Developer Hub
Convex React is the client library enabling your React application to interact
Sandy
SandyOP11mo ago
I seeeee Thank you so much

Did you find this page helpful?