Sort posts by number of votes within certain time frame
-
11-06-2021 - |
Вопрос
I currently have a voting system implemented in my app and I'm sorting the posts by number of votes with this code in my view:
<%= render @posts.sort_by { |post| post.votes.count }.reverse %>
I want to sort by number of votes for each post by also don't want the post to be any more than lets say 5 days old. How can I sort the posts by both number of votes and date simultaneously.
Решение 5
I figured out another way to do it although I appreciate your help it may not be the cleanest way but I did
def most
range = "created_at #{(7.days.ago.utc...Time.now.utc).to_s(:db)}"
@posts = Post.all(:conditions => range)
@title = "All Posts"
@vote = Vote.new(params[:vote])
respond_to do |format|
format.html
format.json { render :json => @users }
end
end
for my controller
created a route of /most :to => 'posts#most'
and made a view with the original code I had in my view.
I know its not the best way but I am still new to coding so its the best way I could figure out how.
Другие советы
This is wrong. You should do all sorting operation on the database side. For this example consider using Arel for creating complex queries or consider create counter cache column.
You could just add a scope to your posts model, something like:
scope :five_days_ago, lambda { where("created_at >= :date", :date => 5.days.ago) }
Then just adjust your render method to the following:
<%= render @posts.five_days_ago.sort_by { |post| post.votes.count }.reverse %>
This assumes you want to keep the structure you are using. Obviously, as other suggested, doing it all in the database is the best course of action.
luacassus is right. It's better do delegate the sorting to the database for at least two reasons:
- Performance
- You can chain more query methods onto it (necessary for pagination, for example).
The counter cache is probably the best idea, but for the complex query, let me give it a shot. In your Post model:
class << self
def votes_descending
select('posts.*, count(votes.id) as vote_count').joins('LEFT OUTER JOIN votes on votes.post_id = posts.id').group_by('posts.id').order('votes_count desc')
end
def since(date)
where('created_at >= ?', date)
end
end
So...
@posts = Post.votes_descending.since(5.days.ago)
Indeed it will be better to let the db do the sorting. I would do something like
class Post < ActiveRecord::Base
default_scope :order => 'created_at DESC'
end
then you will always have your posts sorted and if I'm not mistaken the last one should be the first you get, so that substitutes your 'reverse' call. Then you can use the scope posted above to get only 5 days old posts. Also check that there is an index on the created_at column in the db. You can do this with
SHOW INDEX FROM posts
The db will do this much faster than ruby.