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!
24 Replies
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.Ohhh wait that makes way more sense
and I'm guessing a similar table for saved posts
Yep, exactly
Awesome, thank you
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 queryGotcha, 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?
Yep, that's great
null
is a bit more idiomatic than false, but ultimately that's down to preferenceSweet, 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
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
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.
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.
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?
Nope! Convex queries are reactive, the data will update automatically.
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
You can use vector search and still have reactivity, it’s just down to implementation details. Can you share relevant code?
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.
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
then I'm mapping directly over posts in my client to create cards
Is it because I'm returning "SearchResultVector" instead of a pure post object that it's not reactive?
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.Okay one last question, react says I can't use the useQuery hook inside my handleSubmit function. Any tips on how to resolve this?
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.
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
I seeeee
Thank you so much