按Rails 3中的虚拟属性排序
背景:我有一组可以投票的post。 我想根据他们的“投票得分”对post进行排序,这可以通过以下公式确定:
((@ 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) }
目标:此方法对我的应用程序加载时间造成巨大损失。 有没有更好,更有效的方法来完成这种排序?
如果您使用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版本的分数(如果存在)。 您还可以在实例中缓存分数function,这样您每次要求获得分数时都不必重新计算分数:
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)
-
你不应该使用
sort!
如果您要分配给同一个变量(在这种情况下它是错误的),您应该将排序更改为:@posts.sort!{|a, b| vote_score(b) <=> vote_score(a) }
-
看起来你每次打电话给另一个post时都会计算Post的票数,这个post很可能会对你的加载时间产生影响,你可以使用
counter_cache
来计算每次投票的时间。将它存储在posts表中。 这将使您只需要从posts表中加载一个db查询。
- counter_cache has_many_through sql optimization,减少sql查询次数
- 如何防止ActiveRecord写入数据库?
- Rails:uniq vs. distinct
- Rails“ActiveRecord_Associations_CollectionProxy的未定义方法”
- ActiveRecord find_all_by_X是否保留顺序? 如果不是,应该使用什么呢?
- rails中的活动管理员 – csv限制
- 您将如何使用rails和ActiveRecord中的引用和引用对文章进行建模?
- Rails habtm并找到没有关联的记录
- has_many通过与现有对象/ ActiveRecord的关联