Ecto/Elixir/Phoenix - 获取没有关联记录的记录

Ecto/Elixir/Phoenix - Fetch records with no associated record

我有这些模式:

  schema "players" do
    field :email, :string
    
    has_many :profiles, MyApp.Profile
    has_many :worlds, through: [:profiles, :world]
  end
  schema "worlds" do
    field :name, :string
    
    has_many :profiles, MyApp.Profile
    has_many :players, through: [:profiles, :player]
  end
  schema "settings" do
    field :mode, :string
    
    belongs_to :player, MyApp.Player
    belongs_to :world, MyApp.World
  end

默认情况下,所有玩家在他们创建的每个世界中都应该有一个设置。但是由于我们代码中的逻辑错误,有些玩家在某些世界中没有设置。

现在我正试图找到那些 players 在某个世界中没有现有 settings 记录的人,这样我就可以使用播种器为他们创建默认设置。

我试过这样的解决方法:

query = from profile in Profile

query
|> Repo.all()
|> Enum.each(fn profile ->
  case get_settings(profile.player_id, profile.world_id) do
    nil ->
      create_settings(profile.player_id, profile.world_id)

    _ ->
      :ok
  end
end)

它有效,但我想避免使用 case 语句。它花费了大量的数据库工作。 有什么方法可以使用查询在某些 worlds 中获取那些 players 而没有现有 settings 记录的方法吗?

使用Ecto.Query.join/5 and Ecto.Query.subquery/2或多或少类似于

subquery =
  from profile in Profile,
    join: player in Player,
    on: player.id == profile.player_id,
    join: world in World,
    on: world.id == profile.world_id

query =
  from setting in Settings,
    join: profile in subquery(subquery),
    on: setting.player_id == profile.player_id and
        setting.world_id == profile.world_id 

或者,更简单的是,您可以直接将设置加入 player_idworld_id 上的配置文件。

后者表明您实际上存在设计缺陷。设置和配置文件基本上是同一个实体,表示两次。这就是为什么你有不一致的原因。使用设置中的任何字段丰富配置文件并完全摆脱设置。