Showing users who liked an item in an item list

感情迁移 提交于 2019-12-23 20:33:11

问题


This is an issue that I've deemed impractical to implement but I would like to get some feedback to confirm.

I have a product and users database, where users can like products, the like data is stored in a reference table with just pid and uid.

The client request is to show 3 users who have liked every product in the product listing.

The problem is, its not possible to get this data in one query for the product listing,

How I once implemented and subsequently un-implemented it was to perform a request for the users who have liked the products during the loop through the product list.

ie.

foreach($prods as $row):
$likers = $this->model->get_likers($row->id);
endforeach;

That works, but obviously results in not only super slow product listings, and also creates a big strain on the database/cpu.

The final solution that was implemented was to only show the latest user who has liked it (this can be gotten from a join in the products list query) and have a link showing how many people have liked, and upon clicking on it, opens a ajax list of likers.

So my question is, is there actually a technique to show likers on the product list, or is it simply not possible to execute practically? I notice actually for most social media sites, they do not show all likers on the listings, and do employ the 'click to see likers' method. However, they do show comments per items on the listing, and this is actually involves the same problem doesn't it?

Any help/guidance would be greatly appreciated!

Thanks

Edit: mock up attached on the desired outcome. there would be 30 products per page.


回答1:


By reading your comment reply to Alex.Ritna ,yes you can get the x no. of results with per group ,using GROUP_CONCAT() and the SUBSTRING_INDEX() it will show the likers seperated by comma or whatever separator you specified in the query (i have used ||).ORDER BY clause can be used in group_concat function.As there is no schema information is available so i assume you have one product table one user table and a junction table that maintains the relation of user and product.In the substring function i have used x=3

SELECT p.*,
COUNT(*) total_likes,
SUBSTRING_INDEX(
GROUP_CONCAT( CONCAT(u.firstname,'  ',u.lastname)  ORDER BY some_column DESC SEPARATOR '||'),
'||',3) x_no_of_likers
FROM product p
LEFT JOIN junction_table jt ON(p.id=jt.product_id)
INNER JOIN users u  ON(u.id=jt.user_id)
GROUP BY p.id

Fiddle

Now at your application level you just have to loop through the products and split the x_no_of_likers by separator you the likers per product

foreach($prods as $row):
$likers=explode('||',$row['x_no_of_likers']);
$total_likes= $row['total_likes'];
    foreach($likers as $user):
    ....
    endforeach;
endforeach;

Note there is a default 1024 character limit set on GROUP_CONCAT() but you can also increase it by following the GROUP_CONCAT() manual

Edit from comments This is another way how to get n results per group, from this you can get all the fields from your user table i have used some variables to get the rank for product group ,used subquery for junction_table to get the rank and in outer select i have filtered records with this rank using HAVING jt.user_rank <=3 so it will give three users records per product ,i have also used subquery for products (SELECT * FROM product LIMIT 30 ) so the first 30 groups will have 3 results for each,for below query limit cannot be used at the end so i have used in the subquery

SELECT p.id,p.title,u.firstname,u.lastname,u.thumbnail,jt.user_rank
FROM 
(SELECT * FROM `product` LIMIT 30 ) p
LEFT JOIN 
 (  SELECT j.*,
   @current_rank:= CASE WHEN @current_rank = product_id THEN  @user_rank:=@user_rank +1 ELSE @user_rank:=1 END  user_rank,
   @current_rank:=product_id
    FROM `junction_table` j ,
    (SELECT @user_rank:=0,@current_rank:=0) r   
    ORDER BY product_id 
  ) jt ON(jt.product_id = p.id)
  LEFT JOIN `users` u ON (jt.`user_id` = u.`id`) 
  HAVING jt.user_rank <=3 
  ORDER BY p.id

Fiddle n results per group




回答2:


You should be able to get a list of all users that have liked all products with this sql.

select uid, 
       count(pid) as liked_products
  from product_user
 group by uid
having liked_products = (select count(1) from products);

But as data grows this query gets slow. Better then to maintain a table with like counts that is maintained through a trigger or separately. On every like/dislike the counter is updated. This makes it easy to show the number of likes for each product. Then if the actual users that liked that product is wanted do a separate call (on user interaction) that fetches the specific likes for one product). Don't do this for all products on a page until actually requested.




回答3:


I am assuming the size of both these tables is non-trivially large. You should create a new table (say LastThreeLikes), where the columns would be pid,uid_1,uid_2 and uid_3, indexed by pid. Also, add a column to your product table called numLikes.

For each "like" that you enter into your reference table, create a trigger that also populates this LastThreeLikes table if the numLikes is less than 3. You can choose to randomly update one of the values anyway if you want to show new users once in a while.

While displaying a product, simply fetch the uids from this table and display them back.

Note that you also need to maintain a trigger for the "Unlike" action (if there is any) to re-populate the LastThreeLikes table with a new user id.




回答4:


Problem

The problem is the volume of data. From the point of view that you need two integer value as a answer you should forget about building a heavy query from your n<->n relations table.

Solution

Generates a storable representation using the file_put_contents() with append option each time a user likes a product. I don't have enough room to write the class in here.

public function export($file);

3D array format

array[product][line][user]

Example:

$likes[1293][1][456]=1;
$likes[82][2][656]=1;
$likes[65][3][456]=1;
.
.
.

Number of users who like this particular product:

$number_users_like_this_product = count($likes[$idProduct]);

All idUser who like this particular product:

$users_like_this_product = count($likes[$idProduct][$n]);

All likes

$all_likes = count($likes);

Deleting a like

This loop will unset the only line where $idProduct and $IdUser you want. Since all the variables are unsigned integer it is very fast.

for($n=1, $n <= count($likes[$idProduct]), $n++)
{
 unset($likes[$idProduct][$n][$idUser]);
}

Conclusion

Get all likes will be easy as:

include('likes.php');

P.S If you want to give a try i will be glad to optimize my stuff and share it. I've created the class in 2012.



来源:https://stackoverflow.com/questions/22677781/showing-users-who-liked-an-item-in-an-item-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!