ruby-on-rails - ActiveRecord 获得特定时间段内的评级优胜网站奖

标签 ruby-on-rails activerecord

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

编辑:根据扩展请求为网站记录设置额外的“条件”:

...您仍然可以使用 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/

相关文章:

ruby-on-rails - 如何命名模型\类?

ruby-on-rails - Highcharts 不适用于 wicked_pdf

ruby-on-rails - 您将如何在 Ruby on Rails 应用程序中使用 rSpec 测试观察者?

ruby-on-rails - Rails - group_by

ruby - OCI8 -- "Connect failed because target host or object does not exist"仅在第一次尝试时

ruby-on-rails - 从 Restful 身份验证迁移到 Devise

ruby-on-rails - 编写多行 ActiveRelation 查询的惯用方法是什么?

sql - sql查询中的动态列

ruby-on-rails - Rails Activerecord 将列添加到多个表

ruby-on-rails - 在 ruby​​ on rails 中从三种不同模型中订购数据