Rails 使用联接 table 查找记录并提取单列

Rails find record with join table and pluck single column

我有一个多对多的关系:

Player 
  has_many :team_players
  has_many :teams, through: :team_players

Team
  has_many :team_players
  has_many :players, through: :team_players

TeamPlayer
  belongs_to :team
  belongs_to :player

玩家可以执行的一个动作是加入一个团队。这只是创建了一个新的 TeamPlayer 记录 team_id + player_id(状态为 'active')。当玩家加入团队时,我希望端点响应玩家 + 玩家所在的所有团队。

玩家入队时,我没有取回玩家记录,我用team_id + player_id创建入队记录。

我知道我可以先获取玩家,然后再执行类似 player.teams 的操作,但我想了解如何使用团队记录获取适当的玩家(整个玩家记录),并且仅从团队 table.

中提取 'name' 列

我试过了:Player.find(player_id).joins(:teams).pluck(:name) 但这不起作用。 我认为连接处在错误的位置。我在想这样的事情:

Player.joins(:teams).pluck(:name) 但不确定将玩家 ID 放在哪里。它会是一个 where 子句吗?

我在搜索时也看到了这样的语法:Player.teams.where... 但也不知道如何使用它。

所以基本上我希望最终结果是这样的:

player {
  // player fields...
  teams: ['team-1', 'team-2', etc...],
}

编辑

此查询有效:

Player.where(id: player_id).includes(:teams).pluck(:name).first

但这只是 returns 一组球队名称(球员是其中的一员)。我希望团队列表嵌套在玩家对象中

花心不是你想要的

pluck 用于立即触发数据库查询并仅将 selected 列作为数组获取。除非它调用一个已经加载的关系并且实际上就像 #map.

这是一个基于table的世界

如果你想select从连接的table中的多个值到结果中的单个列,你需要一个聚合函数。例如,在 Postgres 上,您可以 select 所有团队名称的数组:

player = Player.left_joins(:teams)
           .group(:id)
           .select(
             'players.*',
             'json_agg(teams.name) AS team_names'
           ) 
           .find(1)

player.team_names # ["foo", "bar", "baz"]

在其他数据库上,您可能需要使用其他聚合函数。但是,您可能希望保留这种方法,直到您出于性能原因实际需要它并使用 .includes / .eager_load.

Player.eager_load(:teams) 实际上 select 将两列分开 table - 通过实际上 select 为每个团队设置一行以及玩家的列:

SELECT "players"."id" AS t0_r0, 
       "players"."created_at" AS t0_r1, 
       "players"."updated_at" AS t0_r2, 
       "teams"."id" AS t1_r0, "teams"."name" AS t1_r1, 
       "teams"."created_at" AS t1_r2, "teams"."updated_at" AS t1_r3 
FROM "players" 
LEFT OUTER JOIN "team_players" ON "team_players"."player_id" = "players"."id" 
LEFT OUTER JOIN "teams" ON "teams"."id" = "team_players"."team_id" 

结果中的每一行看起来像:

t0_r0 (players.id) | t1_r0 (teams.id) | t1_r1 (teams.name) 
-------------------------------------------------------------
1                  | 1                | Arsenal
1                  | 2                | Tottenham
1                  | 3                | AC Milan
2                  | 4                | AFC Ajax
2                  | 1                | Arsenal

Rails 将实际循环遍历结果并将其全部填充到玩家和团队记录中,以便您可以专注于更有趣的事情:

class Player < ApplicationRecord
  # ...

  def team_names
    teams.map(&:name)
  end
end
player = Player.eager_load(:teams)
                .id(1)
puts player.team_names

范围关联

以上所有仍然会得到所有的团队。如果您只想要活跃的团队,您可以添加其他协会:

class Player < ApplicationRecord
  has_many :team_players
  has_many :teams, through: :team_players
  has_many :active_team_players,
    ->{ where(active: true ) },
    class_name: 'TeamPlayer' 
  has_many :active_teams, 
    through: :active_team_players,
    source: :team
end

当您加入 active_teams 时,它会在加入子句中添加一个条件:

irb(main):003:0> Player.joins(:active_teams)
  Player Load (0.8ms)  SELECT "players".* FROM "players" INNER JOIN "team_players" ON "team_players"."active" =  AND "team_players"."player_id" = "players"."id" INNER JOIN "teams" ON "teams"."id" = "team_players"."team_id"  [["active", true]]
=> []