ActiveRecord:包括组中的关联

问题描述 投票:0回答:2

我真的很喜欢ActiveRecord,但有时候我发现这些(相对)复杂的查询很有挑战性,而且我不知道如何寻求帮助。

这是我的方法:

  def self.get_top_victims_for_player(player, count=10)
    top_victims = player
                  .kills
                  .group(:victim)
                  .order('count_id DESC')
                  .limit(count)
                  .count(:id)

    formatted = top_victims.map do |player, count|
      { "#{player.names.first.name} (#{player.steam_id})" => count}
    end.reduce({}, :merge)

    { type: "list", data: formatted }
  end

目标是获得所提供玩家杀人的受害者的前十名单。

这一行:{ "#{player.names.first.name} (#{player.steam_id})" => count}创建了许多查询来检索每个玩家的相关Names。我觉得如果我能够加载这个Name协会,我可以在一个查询中检索每个玩家的第一个Name

这是运行方法时的日志:

D, [2017-12-18T23:49:11.324957 #26877] DEBUG -- :   Player Load (0.3ms)  SELECT  "players".* FROM "players" WHERE "players"."id" = $1 LIMIT $2  [["id", 304], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.332907 #26877] DEBUG -- :    (5.2ms)  SELECT  COUNT("kills"."id") AS count_id, "kills"."victim_id" AS kills_victim_id FROM "kills" WHERE "kills"."player_id" = $1 GROUP BY "kills"."victim_id" ORDER BY count_id DESC LIMIT $2  [["player_id", 304], ["LIMIT", 10]]
D, [2017-12-18T23:49:11.335472 #26877] DEBUG -- :   Player Load (0.6ms)  SELECT "players".* FROM "players" WHERE "players"."id" IN (735, 96, 98, 893, 335, 213, 2801, 268, 1391, 3108)
D, [2017-12-18T23:49:11.338581 #26877] DEBUG -- :   Name Load (1.1ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 735], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.341112 #26877] DEBUG -- :   Name Load (0.3ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 96], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.343411 #26877] DEBUG -- :   Name Load (0.9ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 98], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.345129 #26877] DEBUG -- :   Name Load (1.0ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 893], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.346893 #26877] DEBUG -- :   Name Load (1.1ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 335], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.348842 #26877] DEBUG -- :   Name Load (1.2ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 213], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.351487 #26877] DEBUG -- :   Name Load (1.6ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 2801], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.354888 #26877] DEBUG -- :   Name Load (1.2ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 268], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.357178 #26877] DEBUG -- :   Name Load (1.6ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 1391], ["LIMIT", 1]]
D, [2017-12-18T23:49:11.358084 #26877] DEBUG -- :   Name Load (0.2ms)  SELECT  "names".* FROM "names" WHERE "names"."player_id" = $1 ORDER BY "names"."id" ASC LIMIT $2  [["player_id", 3108], ["LIMIT", 1]]

阿森松时尚:

  • Player has_many :kills
  • Player has_many :names
  • Kill belongs_to :player
  • Kill belongs_to :victim, :class_name => 'Player', foreign_key: 'victim_id'
  • Name belongs_to :player

如何优化此方法?

(旁注:我在哪里可以了解更多相关信息?当我遇到与此类似的问题时,我应该使用哪些资源?)

ruby-on-rails ruby activerecord
2个回答
0
投票

为什么不创建另一个具有hmt关系的模型,当它们进入时会记录新的杀戮?新型号:UserKilledPlayersbelongs_to :user

在用户模型中:

has_many :user_killed_players     
has_many :killed_players, source: :user_killed_players, through: :users_killed_players

您的表格类似于:user_killed_players

user_id  | killed_players_id |  count

每次用户杀死另一个用户时,在++上制作一个count的方法。然后它只是一个简单的查询

UserKilledPlayer.where(user_id: current_user).order('max(count) DESC')

关于将用户与用户关联的更多细节有很多:here


0
投票

这是一个在一个查询中检索数据的解决方案:

def self.get_top_victims_for_player(player_id, count=10)
  sql_string = <<-EOS
with first_names as 
( select distinct on (player_id) player_id, name 
from names 
order by player_id, id ) 
select players.steam_id, first_names.name, count(kills.id) 
from kills 
join first_names on kills.victim_id = first_names.player_id 
join players on kills.victim_id = players.id 
where kills.player_id = #{player_id} 
group by kills.victim_id, first_names.name, players.steam_id 
order by count(kills.id) desc 
limit #{count}
  EOS
  res = connection.execute(sql_string)
  res.map {|h| {"#{h['name']} (#{h['steam_id']})" => h["count"]}  }
end

使用一些本地数据运行示例:

>> Player.get_top_victims_for_player(1)
   (1.0ms)      with first_names as 
      ( select distinct on (player_id) player_id, name 
      from names 
      order by player_id, id ) 
    select players.steam_id, first_names.name, count(kills.id) 
    from kills 
    join first_names on kills.victim_id = first_names.player_id 
    join players on kills.victim_id = players.id 
    where kills.player_id = 1 
    group by kills.victim_id, first_names.name, players.steam_id 
    order by count(kills.id) desc 
    limit 10

=> [{"name2 (2)"=>5}, {"name3 (3)"=>3}, {"name4 (4)"=>2}]

缺点是它由于CTE和DISTINCT ON而特定于PostgreSQL,并且它还有许多原始SQL。可能它可以通过一些工作制作更多Active Record-ish。

© www.soinside.com 2019 - 2024. All rights reserved.