Search code examples
postgresqlelixirphoenix-frameworkecto

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


I have these schemas:

  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

All players are supposed to have one settings in each world they create by default. But due to logical errors in our code, some players didn't have settings in some world.

Now I'm trying to find those players who don't have existing settings record in some world so I can create default settings for them using a seeder.

I've tried workarounds like this:

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)

It works but I want to avoid using the case statement. It costs a lot of database work. Is there any way to fetch those players with no existing settings record in some worlds using a query?


Solution

  • Use Ecto.Query.join/5 and Ecto.Query.subquery/2 more or less like

    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 
    

    or, even easier, you might directly join settings to profiles on player_id and world_id.

    The latter reveals that you actually have a design flaw. Settings and profiles are basically the same entity, represented twice. That’s why you have inconsistency. Enrich profiles with whatever fields in settings and get rid of settings at all.