按Rails 3中的虚拟属性排序

neo*_*eon 3 sorting activerecord ruby-on-rails ruby-on-rails-3

背景:我有一组可以投票的帖子.我想根据他们的"投票得分"对帖子进行排序,这可以通过以下公式确定:

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

我目前正在定义投票得分:

  def vote_score(x)
   ( (x.votes.count) / ( (Time.now - x.created_at) ** 1 ) )
  end
Run Code Online (Sandbox Code Playgroud)

并将它们排序为:

@posts = @posts.sort! { |a,b| vote_score((b) <=> vote_score((a) }
Run Code Online (Sandbox Code Playgroud)

目标:此方法对我的应用程序加载时间造成巨大损失.有没有更好,更有效的方法来完成这种排序?

Pan*_*kos 14

如果您使用MySQL,您可以使用查询执行整个操作:

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
Run Code Online (Sandbox Code Playgroud)

要么:

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
Run Code Online (Sandbox Code Playgroud)

这将使您的整个查询:

@posts = Post.with_score.all
Run Code Online (Sandbox Code Playgroud)

PS:然后,您可以修改Post类以使用分数的SQL版本(如果存在).您还可以在实例中缓存分数功能,这样您每次要求获得帖子分数时都不必重新计算分数:

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

PS:SQLLite3的等价物是:

strftime('%s','now') - strftime('%s',posts.created_at)
Run Code Online (Sandbox Code Playgroud)