Rails 3 中按虚拟属性排序

发布于 2024-10-21 21:35:58 字数 468 浏览 2 评论 0原文

背景:我有一组可以投票的帖子。我想根据帖子的“投票得分”对帖子进行排序,投票得分由以下等式确定:

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

我是目前将投票分数定义为:

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

并将它们排序为:

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

目标: 此方法对我的应用程序加载时间造成了巨大影响。有没有更好、更高效的方法来完成这种排序?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

酒废 2024-10-28 21:35:58

如果您使用 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

或者:

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

这将生成您的整个查询:

@posts = Post.with_score.all

PS:然后您可以修改您的 Post 类以使用 SQL 版本的 Score(如果存在)。您还可以将分数函数缓存在实例中,这样您就不必在每次请求帖子分数时重新计算它:

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

PS:SQLLite3 等效项是:

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

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)
旧梦荧光笔 2024-10-28 21:35:58
  1. 如果您要分配给同一个变量(在本例中这是错误的),则不应使用sort!,您应该将排序更改为:

    @posts.sort!{|a, b| vote_score(b) <=>; vote_score(a) }
    
  2. 看起来你每次调用另一个帖子时都在计算帖子的票数,该帖子对数据库的访问量很大,并且可能是加载时间的来源,你可以使用 counter_cache 来每次投票时进行计数并将其存储在 posts 表中。这将使您只需执行一个数据库查询即可从 posts 表中加载。

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

  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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文