我有三个型号,User,Venue,Rating,如下:
class User < ActiveRecord::Base
has_many :ratings
end
class Venue < ActiveRecord::Base
has_many :ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :venue
end
用户可以对0-5的场地进行评分。用户可以根据需要随时对场地进行评级,并且通常他们在同一场地内的同一用户的评分不同。
我希望能够提供过去一小时的场地平均评分,但我只想考虑该时段内每位用户的单一评分。因此,如果用户在过去一小时内多次对同一场地进行评分,则只会考虑他们最近的评分。
目前我有这个:
class Venue < ActiveRecord::Base
has_many :ratings
def past_hour_average
ratings = self.ratings.where(:created_at => 1.hour.ago..Time.now).uniq_by(&:user_id)
# loop through each record and compute average
sum = 0
ratings.each do |rating|
sum += rating.value
end
return sum / ratings.size
end
end
然而,这种方法似乎效率低下。每次我想要一个场地的评级我都要计算它。 假设有许多用户经常评价单个场地,那么计算平均评级的更好方法是什么呢?
答案 0 :(得分:1)
我认为这应该有效:
def past_hour_average
ratings = self.ratings.where(created_at: 1.hour.ago..Time.now).order(:created_at).group(:user_id)
ratings.sum(:value) / ratings.count
end
如果从每个用户返回最旧而非最新的评分,您可能只需要撤消订购。
这正是你的代码所做的......它只是让数据库为你做总和,而不是在你的ruby代码中手动计算它。
答案 1 :(得分:1)
您确定此计算的效率甚至会成为一个问题吗?我认为这不太可能,除非您的网站遇到非常非常重读取负载。
但它确实很重要,这是一件你可以做的简单的事情,可能会有所帮助:
def past_hour_average
@past_hour_average ||= begin
# calculation here
end
end
这将确保在单个请求的空间内对单个Venue进行多次计算。
如果您需要更好,并且实际上已经检查过以确保确实是一个问题,您可以缓存计算结果并使缓存无效(如果它早于某个)分钟数。我不打算在这里使用MemCached(等)。我会做类似的事情:
class Venue
@@avg_rating_cache = {}
def past_hour_average
if avg,time = @@avg_rating_cache[self.id] && time > (Time.now - 10.minutes)
@@avg_rating_cache[self.id] = [avg, Time.now]
return avg
end
value = calculation_here
@@avg_rating_cache[self.id] = [value, Time.now]
value
end
end
这将直接将结果缓存在每个应用程序进程的内存中(因此访问MemCached缓存不会有额外的开销/延迟)。如果您有超过10,000个场地,则需要从缓存中逐出条目,因为添加了新条目以防止过多的内存使用。