我的评分系统非常简单,无论是用户喜欢还是他不喜欢这篇文章。
基本上,我有类似的东西,它完美无缺:
class Article
has_many :ratings
def self.by_ratings
all.sort_by{|article| (article.ratings.where(like: true).size).to_f / (article.ratings.size) }
end
end
你可以猜到,我们的应用程序在数据库中变得庞大,流量增加,这部分代码成为瓶颈。
我试图在纯sql中重写它以提高性能。我也试图将它作为ActiveRelation与其他条件链接起来。尝试也写sql但没有成功。有什么想法吗?
谢谢!
使用:
答案 0 :(得分:2)
您需要的是缓存列。
1)创建迁移以支持缓存列:
class AddRatingCacheToArticles < ActiveRecord::Migration
def self.up
add_column :articles, :rating_cache, :decimal, :default => 0.0
end
def self.down
remove_column :articles, :rating_cache
end
end
2)在Article
中定义一个更新方法,用于计算:
class Article < ActiveRecord::Base
has_many :ratings
def update_rating_cache
current_rating = ratings.where(:like => true).count.to_f/ratings.count.to_f
update_attribute(:rating_cache, current_rating)
end
end
3)在Rating
设置回调以在保存时触发update_rating_cache
方法:
class Rating < ActiveRecord::Base
belongs_to :article
after_save :update_article_rating_cache
after_destroy :update_article_rating_cache
def update_article_rating_cache
article.update_rating_cache if article
end
end
4)现在通过评级对文章进行排序非常容易:
class Article < ActiveRecord::Base
has_many :ratings
def self.by_ratings
order('rating_cache DESC')
end
def update_rating_cache
current_rating = ratings.where(:like => true).count.to_f/ratings.count.to_f
update_attribute(:rating_cache, current_rating)
end
end
这可以用作ActiveRelation!
祝你好运:)答案 1 :(得分:1)
我还没能在你的模型中测试这个SQL,但你可以尝试一下:
select articles_ratings.*, likes/total rating from (select articles.*, SUM(if(articles.like, 1, 0)) likes, count(*) total from articles JOIN ratings on article_id = articles.id GROUP BY articles.id) articles_ratings ORDER BY rating desc
这应该有希望给你一个文章列表,按其评级(从最高到最低)排序。如果可行,我可以尝试跟进一些导轨。
编辑正如@socjopa所暗示的那样,如果您不想立即将其投入生产,我的下一个建议是将此查询移至视图中。像对待任何其他ActiveRecord
一样对待它,并相应地将其与您的文章相关联。
使用适当的索引,视图应该相当快,但每次运行时计算评级值可能不是必需的。如果性能不在您需要的位置,您可能还需要考虑在您的文章表中存储评级列。只要修改或创建文章评级,您就可以简单地更新此评级。
也就是说,这个表现应该是当前迭代的日夜。
答案 2 :(得分:1)
从您的by_ratings方法中,我了解您希望文章按最喜欢的评论/评分排序。
我们可以将方法重写为这样的范围:
scope :by_ratings, select('articles.*, ((select count(id) from ratings where article_id = articles.id) - count(article_id) ) as article_diff_count')
.joins(:ratings).group('article_id').where('like = ?',true).order('article_diff_count asc')
我选择比较总评分和喜欢评分之间的difference instead of ratio
,因为这应该在SQL引擎上更轻。希望这会有所帮助。
答案 3 :(得分:1)
我对你的实现做了一些假设,就像我假设你的评级模型中有value
字段,对于“喜欢”可以是1,对于“不喜欢”可以是-1,等等
开始于:
class Article
has_one :rating_value, :class_name => 'RatingValue'
end
class RatingValue < ActiveRecord::Base
belongs_to :article
set_table_name "rating_values"
end
因此,在迁移中生成视图(postgres示例):
execute %q{CREATE OR REPLACE VIEW rating_values AS
SELECT ratings.article_id, sum(ratings.value) AS value
FROM ratings
GROUP BY ratings.article_id;}
如果您有这样的数据库视图,则可以创建排序所需的范围:
scope :ascend_by_rating, {:joins => %Q{
LEFT JOIN "rating_values"
ON rating_values.article_id = article.id },
:order => "rating_values.value ASC"}
应该比在Ruby中的排序尝试更有效率。