活动记录获得特定时间段的评级获奖者网站



有一个Website模型,它has_manyRating具有score属性。

为了确定前 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 个(只有少数)网站,而不是数千条website记录,或者您不打算在数千个数组中迭代每个。

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对象的其他属性(如idscore)将全部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排名检查器,我不太确定如何实现它。

编辑:每个扩展请求为Website记录提供额外的"条件":

。您仍然可以使用joins(:website)但与eager_load(:website)includes(:website)相比,这不会阻止 N+1 个查询,但由于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

最新更新