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]]
=> []
我有一个多对多的关系:
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]]
=> []