How would I implement a star rating system into my Database design? - mysql

I am currently making an Android app where I want to allow users to rate posts, posted by other users.
This is my current design, however, would I include Rating as a separate table or included in the Media upload table?
If it is a separate table would it be something like this?
Rating table: (Rating, PostID, Rating_Count, Average_Rating)
If anything else looks wrong with my design that would also be appreciated!

It all depends on how you want the ratings to work, who gets to up-vote or down-vote, whether you track everyone that votes to keep from having multiple votes from the same person, etc.
This is what I would do: add a unique numeric (long?) RatingID field for each vote, link the PostID field to the Comments table, add a Rating (integer) field that is limited to values from 0 to 5 (or whatever range you prefer), and then calculate the average from all votes cast (delete the Rating_Count and Average_Rating fields). You could define a view to calculate the average ratings for each post to use for your routine for determining how to display it next to the Post.
Also, I would use an ID for each user, not use their names.
So my table would look like this:
RatingID, PostID, UserID, Rating
Also, to keep users from voting multiple times, the table would not allow multiple entries for the same PostID and UserID.

It depends on how you want to store the Rating.
If you want to track each individual rating. You will need a Rating table with the following:
Column
---------
RatingID (PK)
Username (FK) (UserDetailsID if you change the primary key of the UserDetails table)
PostID (FK)
RatingScore
DateRated
In the app, you would then pull back every RankScore with that PostID and do the calculation based off a count of said PostID.
I would recommend the above method because it will allow you do more things with the Ratings in the future. Plus, you have a good structure to follow in place with your Comments table. ex. Show who Rated what and at what score.
If you want to attach it to the Post table, another table is not needed. You would simply add Rating_Count and Average_Rating to the table. In your app, you would than have to perform an update every time the Post is rated. You would have to pull the Rating_Count and Average_Rating, increment the Rating_Count by one, recalculate the Average_Rating and perform the update.
My second suggestion is less flexible if you ever want to enhance your Rating setup though.

you can use stretcher like this
enter code here
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->ipAddress('ip')->nullable();
$table->integer('star');
$table->string('type');
$table->string('title');
$table->string('average')->nullable()->default(0);
$table->integer('count_star')->default(0)->nullable();
$table->float('star_avg')->default(0)->nullable();
$table->float('unstar_avg')->default(0)->nullable();

Related

Liked Posts Design Specifics

So I've found through researching myself that the best way I can design a structure for liking posts is by having a database like the following. Let's say like Reddit, a post can be upvoted, downvoted, or not voted on at all.
The database would then having three columns, [username,post,liked].
Liked could be some kind of boolean, 1 indicating liked, and 0 indicating disliked.
Then to find a post like amount, I would do SELECT COUNT(*) FROM likes WHERE post=12341 AND liked=1 for example, then do the same for liked=0(disliked), and do the addition server side along with controversy percentage.
So I have a few concerns, first off, what would be the appropriate way to find out if a user liked a post? Would I try to select the liked boolean value, and either retrieve or catch error. Or would I first check if the record exist, and then do another select to find out the value? What if I want to check if a user liked multiple posts at once?
Secondly, would this table not need a primary key? Because no row will have the same post and username, should I use a compound primary key?
For performance you will want to alter your database plans:
User Likes Post table
Fields:
Liked should be a boolean, you are right. You can transform this to -1/+1 in your code. You will cache the numeric totals elsewhere.
Username should be UserID. You want only numeric values in this table for speed.
Post should be PostID for the same reason.
You also want a numeric primary key because they're easier to search against, and to perform sub-selects with.
And create a unique index on (Username, Post), because this table is mainly an index built for speed.
So did a user vote on a post?
select id
from user_likes_post
where userID = 123 and postID = 456;
Did the user like the post?
select id
from user_likes_post
where userID = 123 and postID = 456 and liked = true;
You don't need to worry about errors, you'll either get results or you won't, so you might as well go straight to the value you're after:
select liked from user_liked_post where userID=123 and postID=456
Get all the posts they liked:
select postID
from user_likes_post
where userID = 123 and liked = true;
Post Score table
PostID
TotalLikes
TotalDislikes
Score
This second table will be dumped and refreshed every n minutes by calculating on the first table. This second table is your cached aggregate score that you'll actually load for all users visiting that post. Adjust the frequency of this repeat dump-and-repopulate schedule however you see fit. For a small hobby or student project, just do it every 30 seconds or 2 minutes; bigger sites, every 10 or 15 minutes. For an even bigger site like reddit, you'd want to make the schema more complex to allow busier parts of the site to have faster refresh.
// this is not exact code, just an outline
totalLikes =
select count(*)
from user_likes_post
where postID=123 and liked=true
totalDislikes =
select count(*)
from user_likes_post
where postID=123 and liked=false
totalVotes = totalLikes + totalDislikes
score = totalLikes / totalVotes;
(You can simulate an update by involving the user's localStorage -- client-side Javascript showing a bump-up or down on the posts that user has voted on.)
Given your suggested 3-column table and the selects you suggest, be sure to have
PRIMARY KEY(username, post) -- helps with "did user like a post"
INDEX(post_id, liked) -- for that COUNT
When checking whether a user liked a post, either do a LEFT JOIN so that you get one of three things: 1=liked, 0=unliked, or NULL=not voted. Or you could use EXISTS( SELECT .. )
Tables need PKs.
I agree with Rick James that likes table should be uniquely indexed by (username, post) pair.
Also I advise you to let a bit redundancy and keep the like_counter in the posts table. It will allow you to significantly reduce the load on regular queries.
Increase or decrease the counter right after successful adding the like/dislike record.
All in all,
to get posts with likes: plain select of posts
no need to add joins and aggregate sub-queries.
to like/dislike: (1) insert into likes, on success (2) update posts.like_counter.
unique index prevents duplication.
get know if user has already liked the post: select from likes by username+post pair.
index helps to do it fast
My initial thought was that the problem is because boolean type is not rich enough to express the possible reactions to a post. So instead of boolean, you needed an enum with possible states of Liked, Disliked, and the third and the default state of Un-reacted.
Now however it seems, you can do away with boolean too because you do not need to record the Un-reacted state. A lack of reaction means that you do not add the entry in the table.
What would be the appropriate way to find out if a user liked a post?
SELECT Liked
FROM Likes
WHERE Likes.PostId == 1234
AND Likes.UserName == "UniqueUserName";
If the post was not interacted with by the user, there would be no results. Otherwise, 1 if liked and 0 if disliked.
What if I want to check if a user liked multiple posts at once?
I think for that you need to store a timestamp too. You can then use that timestamp to see if it there are multiple liked post within a short duration.
You could employ k-means clustering to figure if there are any "cluster" of likes. The complete explanation is too big to add here.
Would this table not need a primary key?
Of course it would. But Like is a weak entity depending upon the Post. So it would require the PK of Post, which is the field post (I assume). Combined with username we would have the PK because (post, username) would be unique for user's reaction.

Best way to store ordered lists in a database?

What's the best way to store "ordered lists" in a database, so that updating them (adding, removing and changing the order of entries) is easily done?
Consider a database where you have a table for users and movies. Each user has a list of favorite movies.
Since many users can like the same movie, I made users and movies separate tables and uses a third table to connect them, usermovies.
usermovies contains an id of a user and a movie and an "order number". The order number is used to order the list of movies for users.
For example, user Josh might have the following list:
Prometheus
Men in Black 3
The Dictator
and user Jack might have a list like:
The Dictator
Prometheus
Battleship
Snow White and the Huntsman
So, they share some favorites, but not necessarily in the same order.
I can get the list of movie IDs for each user using a query:
SELECT movie_id FROM usermovies WHERE user_id =? ORDER BY order_number
Then, with the ordered movie_ids, I can get the list of movies using another query
SELECT name FROM movies WHERE id in (?,?,?) ORDER BY FIELD (id, ?,?,?)
So queries work, but updating the lists seems really complex now - are there better ways to store this information so that it would be easy to get the list of movies for user x, add movies, remove them and change the order of the list?
If you are not looking for a "move up / move down" kinda solution, and then defaulting to adding at the bottom of the list, here are a few more pointers:
Inserting new rows into a specific position can be done like this: (inserting at position 3)
UPDATE usermovies SET order_number = ordernumber + 1
WHERE ordernumber > 3 and user_id = ?;
INSERT INTO usermovies VALUES (?, 3, ?);
And you can delete in a similar fashion: (deleting position 6)
DELETE usermovies WHERE order_numer = 6 and user_id=?;
UPDATE usermovies SET order_number = ordernumber - 1
WHERE ordernumber > 6 and user_id = ?;
A junction/link table with additional columns for the attributes of the association between movies and users is the standard way of realizing a many-many association with an association class - so what you have done seems correct.
Regarding the ease of insert/update/delete, you'll have to manage the entire association (all rows for the user-movie FKs) every time you perform an insert/update/delete.
There probably isn't a magical/simpler way to do this.
Having said this, you'll also need to run these operations in a transaction and more importantly have a 'version' column on this junction table if your application is multi-user capable.
To retrieve user favourites movies you could use a single query:
SELECT um.order_number, m.name FROM movies m
INNER JOIN usermovies um ON m.id = um.movie_id
WHERE um.user_id = ?
ORDER BY um.order_number
To add/remove a favourite movie simply add/remove related record in usermovies table.
To alter a movie order simply change all order_number field in user_movies table related to user.
In addition to what others have said, reordering existing favorites can be done in a single UPDATE statement, as explained here.
The linked answer explains reordering of two items, but can be easily generalized to any number of items.

MySQL Database design and effecient query

I have the following tables:
users (id, first_name, last_name)
category (id, name)
rank(id, user_id, rank)
Each user can belong to several categories. And all users are in the rank table and have a value between 0.0 and 1.0, where 0 is the lowest rank and 1 is the highest. I’d like to setup additional tables to create the following webpage:
A visitor to the page (identified by either one of the recorded ids in the user table, or a numeric representation of their ip address) chooses a category and is presented with two randomly chosen users from the users table such that:
1) the visiting user_id has not seen this pairing in a period of 24 hours
2) the two users belong to the chosen category
3) the two users are within 1 rank value of each other. Let me explain that last criteria - if the ranks were sorted, the two chosen users would have adjacent ranks.
This is a hard one and I can’t for the life of me figure it out how to do this effeciently
I truly appreciate any help on this front.
Thanks
You just need two more tables and the rest go in your website logic.
user_category(user_id, category_id)
user_pairing(first_user_id, second_user_id, last_seen)
The first table is to represent a ManyToMany relationship between the users and the category, and the second one is for the users pairing.
I agree with #Yasel, i want to add that you properly want another table
candidate(first_user_id, second_user_id);
this table is used to pre-calculate the candidates for each user, this candidate table is prepopulated every hour/day, so when each first_user_id, second_user_id is assigned, this pair is removed from candidate table and moved into user_pairing table. so each time you only need to query candidate table which should be efficient.

Like and Dislike System for Posts

I want to include a like/dislike system similar to Facebook and so far, I have set the like/dislike columns as a 'text' type. This is so that I can add the id for the user(s) who liked/disliked a post. Would that be the best way of doing it? Also, in addition to the question above, how would I stop a user pressing the like and dislike button again? Since, once a user has liked a post, it should display an unlike/undislike option? A concept/idea would be great of how to do this.
While it's hard to make armchair decisions, here are my ideas:
First, you could have a 'likes' integer column for each post. When a user clicks up and down, have that number increment or decrement. This offers no protection against users clicking multiple times, but it's easy and fast.
Another way would be to have a 'Like' table, with columns post_id, user_id, and score. score can have two values: '1' or '-1'. All 3 columns are integers. When the user clicks 'like', you do an INSERT/UPDATE command on the row with user_id & post_id matching.
Then, to see the final score for a post, you do a SELECT SUM(score) FROM that_table WHERE post_id = ?.
With this second method, if you wanted to see the name of the most recent clicker, you could add a timestamp column and search for the most recent entry.
I would create a table with the following structure:
Table: Likes
PostId bigint
UserId bigint
Like bit(true, false)
Then set PostId and UserId together as the primary key. This will prevent the database from inserting multiple likes/unlikes for the same post.
In your code, check to see if the user/post combination exists and then toggle the bit value if it does or set the bit value to true if it does not.

building activity feed

I want to create some kind of 'activity feed'. For example, There are total 1000 users in database, of which there are 100 people in contact list of user X, who is concerned with those 100 users only, and want that if any of them posts a note (in general, takes an action), he wants to get that update on my page. For this purpose, do i need to make a database table, like:
id user_id note_id
In this table, there will be users which are not concerned to user X, so I will make some query like,
select user_id from activity_table which exists in contact list of user X
Is my approach correct regarding this matter (for example database table design and query)?
Is there any better approach?
If I understand you correctly I think you need a relation table where you will store user_ids of the user that is being concerned and of the user that concerns.