背景:我有一组可以投票的帖子。我想根据他们的“投票得分”对帖子进行排序,这可以通过以下公式确定:
((@ 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) }
目标:此方法会严重影响我的应用加载时间。是否有更好,更有效的方法来完成这种排序?
答案 0 :(得分: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
或者:
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
P.S:然后,您可以修改Post类以使用分数的SQL版本(如果存在)。您还可以在实例中缓存分数功能,这样您每次要求获得分数时都不必重新计算分数:
class Post
def score
@score ||= self[:score] || (votes.count/(Time.now.utc - x.created_at.utc)
end
end
P.S:SQLLite3的等价物是:
strftime('%s','now') - strftime('%s',posts.created_at)
答案 1 :(得分:0)
如果您要分配给同一个变量(在这种情况下它是错误的),则不应使用sort!
,您应该将排序更改为:
@posts.sort!{|a, b| vote_score(b) <=> vote_score(a) }
看起来你每次打电话给另一个帖子时都会计算Post的票数,这个帖子很可能会对你的加载时间产生影响,你可以使用{{1}每次进行投票时计算并将其存储在posts表中。这将使你只需要从posts表中加载一个db查询。