有一个 Website
模型,其中有多个带有 score
属性的 Rating
。
要确定前 10 个获胜网站,我只需计算并缓存网站记录上的 total_score
并排序。
不幸的是,这并不那么容易 - 我需要在特定的时间范围内确定奖项的获奖者。可以在奖励时间段之前和之后进行评级。
我该怎么做?分组然后按计数排序(这不起作用 - 见下文)?我猜它有大约 1.000 条网站记录,总共有 100.000 个评分...
Website.left_joins(:ratings).group('ratings.website_id').
where(ratings: { created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01') }).
order('count(ratings.score) desc').
limit(10)
或者创建类似那个时期快照的东西?
任何建议都会很棒!
最佳答案
如果没有连接/子查询:
即如果您只检索前 10 个(仅少数)网站,而不是数千条 网站
记录,或者您不打算迭代数千个数组中的每一条记录。
ratings = Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(score) desc'
).select(
:website_id
)
top_10_winner_websites = ratings.take(10).map(&:website)
# above is ordered by sum(score) desc
# so,
# top_10_winner_websites[0] has the highest sum score, while
# top_10_winner_websites[-1] has the lowest sum score amongst the 10
注意:请注意,上面的查询只是“选择” ratings.website_id
,而不是 ratings。*
这意味着 ratings
对象的其他属性(如 id
和 score
)将全部为 nil
,仅 website_id
如果使用连接/子查询:
编辑:下面的 TODO 尚未完全工作;可能需要帮助。无法找到/解决在子查询外部保留 website_id
顺序的方法。这会儿很忙。
如果您要迭代每个 website
记录,或者要检索数千条 website
记录,则可以防止 N+1 查询。
top_10_winner_websites = Website.where(
id: Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
'website_id r'
)
).limit(10).order('r.website_id asc')
当前解决方法(对于上面未完成的子查询):
作为解决子查询外部“保留顺序”的解决方法,同时防止 N+1 查询:
ratings = Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
:website_id
)
top_10_winner_website_ids = ratings.take(10).map(&:website_id)
top_10_winner_websites = Website.where(
id: top_10_winner_website_ids
).sort_by do |website|
top_10_winner_website_ids.index(website.id)
end
编辑:根据扩展请求,您可以检查并获取网站的排名:
website_to_check = Website.find(1)
index = top_10_winner_websites.index{|winner_website| winner_website.id == website_to_check.id }
if index.nil?
puts 'Website was not part of the top 10 winners in this time period'
else
puts "Website rank for this time period was: #{index + 1}"
end
^ 如果您想要一个纯 SQL 排名检查器,我不太确定如何实现它。
编辑:根据扩展请求为网站
记录设置额外的“条件”:
...您仍然可以使用 joins(:website)
但与 eager_load(:website)
或 includes 相比,这不会阻止 N+1 查询(:website)
但由于 PG::GroupingError
,急切加载似乎不起作用,但您仍然可以使用上面的解决方法来阻止 N+1 查询。请参阅下面的完整示例:
ratings = Rating.joins(:website).where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01'),
websites: {
some_website_attribute_1: true, # UPDATE THIS
some_website_attribute_2: 'foobar' # UPDATE THIS
}
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
:website_id
)
top_10_winner_website_ids = ratings.take(10).map(&:website_id)
top_10_winner_websites = Website.where(
id: top_10_winner_website_ids
).sort_by do |website|
top_10_winner_website_ids.index(website.id)
end
关于ruby-on-rails - ActiveRecord 获得特定时间段内的评级优胜网站奖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56539312/