Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails activerecord : sum, max and joins

I have two models users and posts. An user can votes and views a post

#users
  id
  name

#posts
  id
  count_votes
  count_views
  users_id
  created_at
  updated_at

I want the user who received the most votes and views on his posts from the last 24 hours. The biggest sum of views and votes win.

WHAT I TRIED I have this SQL query, it's good but I would like to have the user with the max of votes, this one give me all users and I don't know how to add count_views

select u.name as "Name", sum(p.count_votes)
from posts p 
inner join users u on p.user_id = u.id 
where p.created_at >= DATE_SUB(NOW(), INTERVAL 1 day)
group by user_id;

ActiveRecord version

Post.select("users.id, sum(posts.count_votes) as nb_votes")
.joins(:user).where("posts.created_at >= ?", 1.day.ago.beginning_of_day)
.group("posts.user_id")

# Results on IRB
=> #<ActiveRecord::Relation [#<Post id: 1>, #<Post id: 3>]> 

How can I combine a sum and a max on these two sums ? Is there a way to have an activerecord code or only raw SQL ?

like image 436
Ludovic Avatar asked Mar 20 '23 05:03

Ludovic


2 Answers

You current query does a grouping on user. So you would get one record for each user in the output. By limiting the output to just 1 record and ordering by votes+views total count, you can get the top user.

Raw SQL:

select u.id, sum(p.count_votes + p.count_views) total
from posts p 
inner join users u on p.user_id = u.id 
where p.created_at >= DATE_SUB(NOW(), INTERVAL 1 day)
group by u.id
order by total DESC
limit 1 ;

ActiveRecord version: Start from your User model, so you will get the user object in the output instead of Post object, like you have mentioned in the question.

User.select("users.id, sum(posts.count_votes + posts.count_views) as nb_votes")
.joins(:post).where("posts.created_at >= ?", 1.day.ago.beginning_of_day)
.group("posts.user_id").order("nb_votes DESC").limit(1)
like image 200
Lenin Raj Rajasekaran Avatar answered Mar 27 '23 22:03

Lenin Raj Rajasekaran


ActiveRecord makes sense once you work with an object, in that case you need to have a User object only, so here is your solution below:

  sql = %Q(SELECT * FROM users 
  WHERE id = (SELECT users_id FROM posts
    WHERE DATE(created_at) = CURDATE()
    ORDER BY count_votes DESC, count_views DESC
    LIMIT 1
  ) 
  LIMIT 1)

  ActiveRecord::Base.connection.execute(sql)
like image 35
Anatoly Avatar answered Mar 27 '23 22:03

Anatoly