Sorting by a virtual attribute in Rails 3

我怕爱的太早我们不能终老 提交于 2020-01-01 00:43:11

问题


BACKGROUND: I have a set of Posts that can be voted on. I'd like to sort Posts according to their "vote score" which is determined by the following equation:

( (@post.votes.count) / ( (Time.now - @post.created_at) ** 1 ) )

I am currently defining the vote score as such:

  def vote_score(x)
   ( (x.votes.count) / ( (Time.now - x.created_at) ** 1 ) )
  end

And sorting them as such:

@posts = @posts.sort! { |a,b| vote_score((b) <=> vote_score((a) }

OBJECTIVE: This method takes a tremendous toll on my apps load times. Is there a better, more efficient way to accomplish this kind of sorting?


回答1:


If you are using MySQL you can do the entire thing using a query:

SELECT   posts.id,
         (COUNT(votes.id)/(TIME_TO_SEC(NOW()) - TIME_TO_SEC(posts.created_at))) as score
FROM     posts INNER JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
ORDER BY score DESC

Or:

class Post
  scope :with_score, select('posts.*')
    .select('(COUNT(votes.id)/(TIME_TO_SEC(NOW()) - TIME_TO_SEC(posts.created_at))) as score')
    .joins(:votes)
    .group('posts.id')
    .order('score DESC')
end

Which would make your entire query:

@posts = Post.with_score.all

P.S: You can then modify your Post class to use the SQL version of score if it is present. You can also make the score function cached in an instance so you don't have to re-calculate it every time you ask for a post's score:

class Post
  def score
    @score ||= self[:score] || (votes.count/(Time.now.utc - x.created_at.utc)
  end
end

P.S: The SQLLite3 equivalent is:

strftime('%s','now') - strftime('%s',posts.created_at)



回答2:


  1. You shouldn't use sort! if you are going to assign to the same variable (it is wrong in this case), you should change the sort to:

    @posts.sort!{|a, b| vote_score(b) <=> vote_score(a) }
    
  2. It looks like you are counting the votes for Post each time you call another Post which is hitting the database quite a bit and probably the source of the toll on your load times, you can use a counter_cache to count each time a vote is made and store that in the posts table. This will make it so you only do one db query to load from the posts table.

http://guides.rubyonrails.org/association_basics.html



来源:https://stackoverflow.com/questions/5292624/sorting-by-a-virtual-attribute-in-rails-3

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