Ecto: cannot use belongs_to field in get_by clause

I defined the following sample tables:

create table("foo") do
  timestamps()
end

create table("bar") do
  add :some_field, :string, null: false
  add :some_ugly_foreign_key_from_a_legacy_db, references("foo"), null: false
  timestamps()
end

create(unique_index("bar", [:some_field, :some_ugly_foreign_key_from_a_legacy_db]))

The corresponding schemas:

schema "foo" do
  timestamps(type: :utc_datetime)
end

schema "bar" do
  field(:some_field, :string)
  timestamps(type: :utc_datetime)
  belongs_to(:foo, MyApp.Test.Foo, foreign_key: :some_ugly_foreign_key_from_a_legacy_db)
end

I deliberately didn’t add a has_many relation in Foo because I do not want to have Bar entities when fetching Foo.

However, I want to be able to get a Bar entity by a Foo and some_field (there is a unique constraint on both fields so I will have at most one row):

foo = List.first(Repo.all(MyApp.Test.Foo))

Repo.get_by!(MyApp.Test.Bar,
  foo: foo, # I also tried foo: foo.id
  some_field: "hello")

The code above won’t work, the error is as follow:

** (Ecto.QueryError) deps/ecto/lib/ecto/repo/queryable.ex:382: field foo in where is a virtual field in schema MyApp.Test.Bar in query

It looks like I have to explicitly write the ugly foreign key name from the actual database as so:

Repo.get_by!(MyApp.Test.Bar,
  some_ugly_foreign_key_from_a_legacy_db: foo.id,
  some_field: "hello")

The database foreign key name is leaked out into the application.
Worse, the returned Bar struct, will contain and leak out the some_ugly_foreign_key_from_a_legacy_db field in the struct:

%MyApp.Test.Bar{
  __meta__: #Ecto.Schema.Metadata<:loaded, "bar">,
  foo: #Ecto.Association.NotLoaded<association :foo is not loaded>,
  id: "4d2a09fd-ace3-4ac2-a9d9-ba0b28af2f3d",
  inserted_at: #DateTime<2019-06-20 05:40:41Z>,
  some_field: "hello",
  some_ugly_foreign_key_from_a_legacy_db: "61ee73cf-cf31-4ac4-b212-d2ca25553662",
  updated_at: #DateTime<2019-06-20 05:40:41Z>
}

Why can’t I use the belongs_to field named foo from the schema? Why does Ecto force me to write the foreign key name inside the database when using a Repo function?

Ecto is meant to interact with your database and map to it. It’s not meant to hide the database from your application. However ecto can do mapping of runtime field names to database column names.

belongs_to(:foo, MyApp.Test.Foo, source: "some_ugly_foreign_key_from_a_legacy_db")

This should define :foo_id as a field with some_ugly_foreign_key_from_a_legacy_db as its source.

2 Likes

So it seems what I really wanted was to specify source: which will set the name of the field in the database. I thought this is what foreign_key: does. However foreign_key:, if I understand correctly, sets the key of the schema structure. Consequently, my code was just wrong. However, I’m really not convinced by that naming :source vs :foreign_key, I think it is possible to find better naming to avoid this confusion.

Or at least improve the documentation:

:source - The source for the underlying field

What is meant by source? Not clear.

:foreign_key - Sets the foreign key field name, defaults to the name of the association suffixed by _id . For example, belongs_to :company will define foreign key of :company_id

I thought that by field name, it was meant database field name name. Especially that “foreign key” is really specific to databases.

You’re free to improve the docs. Just a bit of context: the source option was only added in one of the later mayor releases of ecto and especially as option for field, where there is no ambiguity. For belongs_to there probably already was the setting of foreign_key, which rather means “the foreign key field to be added to the schema aside from the full assoc field”. Without source both names are the same, but only when adding the source option the complexity of differenciating both is introduced.

I want to add two things:

  1. source value must be an atom. Specifying a string (source: "some_ugly_foreign_key_from_a_legacy_db") will lead to an error:

(FunctionClauseError) no function clause matching in Ecto.Adapters.Postgres.Connection.expr/3
The following arguments were given to Ecto.Adapters.Postgres.Connection.expr/3:

  1. If foreign_key is specified, it will also set the source value. E.g.
    belongs_to(:foo, Test.Foo, foreign_key: :my_custom_key_name)
    Here foreign_key is specified without source, so Ecto considers the field name in database is equal to my_custom_key_name.
    As Ecto likes explicit stuff I would have rather preferred that foreign_key doesn’t change the database field name. As we said, foreign_key customizes the key for the structure. To me, it seems better to force the developer to specify source if he wants to change the field in the database, and clearly make that distinction foreign_key option for the struct vs source option for the db.

That isn’t how it works.

Ecto only preloads if you specifically request it. By not specifying has_many you are denying yourself the use of assoc/2 which is designed to hide that

some_ugly_foreign_key_from_a_legacy_db field


Example:

Given Album:

defmodule MusicDB.Album do
  use Ecto.Schema
  alias MusicDB.{Artist, Track, Genre}

  schema "albums" do
    field(:title, :string)
    timestamps()

    belongs_to(:artist, Artist)
    has_many(:tracks, Track)
    many_to_many(:genres, Genre, join_through: "albums_genres")
  end

end

and Track:

defmodule MusicDB.Track do
  use Ecto.Schema
  alias MusicDB.Album

  schema "tracks" do
    field(:title, :string)
    field(:duration, :integer)
    field(:duration_string, :string, virtual: true)
    field(:index, :integer)
    field(:number_of_plays, :integer)
    timestamps()

    belongs_to(:album, Album)
  end
end

Script:

defmodule Playground do
  import Ecto.Query
  alias MusicDB.Repo
  alias MusicDB.{Album, Track}

  def play do
    # TLDNR
    # Get Album with a specific track preloaded
    # result: one %MusicDB.Album{} that CONTAINS
    #   the %MusicDB.track{} the meets the specified criteria
    #
    # https://hexdocs.pm/ecto/Ecto.Query.html#preload/3
    #
    # Note: this executes 2 queries!
    #
    album_id = 3
    track_index = 2

    preload_query = from(t in Track, where: t.index == ^track_index)

    tldnr_query =
      from(a in Album,
        where: a.id == ^album_id,
        preload: [tracks: ^preload_query]
      )

    show_results(tldnr_query, "====== TLDNR QUERY")

    # A particular album
    # result: one %MusicDB.Album
    #   and no tracks are preloaded
    #
    album_id = 3
    query = from(a in Album, where: a.id == ^album_id)
    show_results(query, "====== JUST THE ONE ALBUM")

    # Compose a join on the existing query
    # i.e. tracks associated with the Album
    # result: one %MusicDB.Album{} per track on the album
    #   and all of them are identical
    #   still no tracks preloaded
    #
    # Note - :tracks refers to Album.ex schema:
    #   has_many(:tracks, Track)
    query1a = join(query, :inner, [a], t in assoc(a, :tracks))
    show_results(query1a, "====== THE SAME ALBUM FOR EACH OF ITS TRACKS (with ASSOC)")

    # Compose an explicit join instead
    # result: identical to previous query
    #
    query1 = join(query, :inner, [a], t in Track, on: a.id == t.album_id)
    show_results(query1, "====== THE SAME ALBUM FOR EACH OF ITS TRACKS (with EXPLICIT JOIN)")

    # Compose a select to show track instead
    # result: all the %MusicDB.Track{} that belong to the album
    #   but no %MusicDB.Album{} anywhere
    #
    query2t = select(query1, [a, t], t)
    show_results(query2t, "====== TRACKS INSTEAD OF ALBUMS")

    # Compose to retrieve Album and Track side by side
    # result: For each track found,
    #   there is a {%MusicDB.Album{}, %MusicDB.Track{}} tuple
    #
    query2at = select(query1, [a, t], {a, t})
    show_results(query2at, "====== ALBUM WITH EACH TRACK")

    # Compose to show only the track at a particular index
    # result: one {%MusicDB.Album{}, %MusicDB.Track{}} tuple
    #   for the track at specified index (provided it exists)
    #
    query3 = where(query2at, [a, t], t.index == ^track_index)
    show_results(query3, "====== ALBUM AND TRACK")

    # Compose (with query) to preload ALL tracks
    # result: %MusicDB.Album{} CONTAINING ALL IT'S %MusicDB.Track{}
    #
    # Note: this executes 2 queries
    #
    query4 = preload(query, [a], :tracks)
    show_results(query4, "====== ALL TRACKS CONTAINED BY ALBUM")

    # Compose (with query) to preload a particular track INTO the album
    # result: %MusicDB.Album{} CONTAINING ONE %MusicDB.Track{}
    #
    # Note: this executes 2 queries
    #
    track_query = from(t in Track, where: t.index == ^track_index)
    query5 = preload(query, tracks: ^track_query)
    show_results(query5, "====== ONE TRACK CONTAINED BY ALBUM")

    :ok
  end

  defp show_results(query, label) do
    query
    |> Repo.all()
    |> IO.inspect(label: label)

    :ok
  end
end

IO.inspect(Playground.play())

1 Like

Results:

$ mix run ./priv/repo/playground.exs
asn1: 5.0.9, compiler: 7.4.1, connection: 1.0.4, crypto: 4.5.1, db_connection: 2.0.5, decimal: 1.6.0, ecto: 3.0.6, ecto_sql: 3.0.5, elixir: 1.8.2, hex: 0.19.0, inets: 7.0.8, jason: 1.1.2, kernel: 6.4, logger: 1.8.2, mariaex: 0.9.1, mix: 1.8.2, music_db: 0.1.0, postgrex: 0.14.1, public_key: 1.6.7, ssl: 9.3.1, stdlib: 3.9.1, telemetry: 0.3.0

11:30:08.264 [debug] QUERY OK source="albums" db=4.7ms decode=0.6ms queue=1.0ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
WHERE (a0."id" = $1) [3]

11:30:08.270 [debug] QUERY OK source="tracks" db=0.7ms queue=1.2ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id", t0."album_id"
FROM "tracks" AS t0
WHERE (t0."index" = $1) AND (t0."album_id" = $2)
ORDER BY t0."album_id" [2, 3]

====== TLDNR QUERY: [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: [
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 337,
        duration_string: nil,
        id: 12,
        index: 2,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "You Must Believe In Spring",
        updated_at: ~N[2019-05-03 00:16:32]
      }
    ],
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.276 [debug] QUERY OK source="albums" db=1.6ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
WHERE (a0."id" = $1) [3]

====== JUST THE ONE ALBUM: [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.279 [debug] QUERY OK source="albums" db=1.0ms queue=0.9ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
  INNER JOIN "tracks" AS t1 ON t1."album_id" = a0."id"
WHERE (a0."id" = $1) [3]

====== THE SAME ALBUM FOR EACH OF ITS TRACKS (with ASSOC): [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2019-05-03 00:16:32]
  },

...

  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.282 [debug] QUERY OK source="albums" db=1.2ms queue=0.9ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
  INNER JOIN "tracks" AS t1 ON a0."id" = t1."album_id"
WHERE (a0."id" = $1) [3]

====== THE SAME ALBUM FOR EACH OF ITS TRACKS (with EXPLICIT JOIN): [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2019-05-03 00:16:32]
  },

...

  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.285 [debug] QUERY OK source="albums" db=1.0ms queue=0.9ms
SELECT t1."id", t1."title", t1."duration", t1."index", t1."number_of_plays", t1."inserted_at", t1."updated_at", t1."album_id"
FROM "albums" AS a0
  INNER JOIN "tracks" AS t1 ON a0."id" = t1."album_id"
WHERE (a0."id" = $1) [3]

====== TRACKS INSTEAD OF ALBUMS: [
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 489,
    duration_string: nil,
    id: 20,
    index: 10,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "All of You",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 454,
    duration_string: nil,
    id: 19,
    index: 9,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "Freddie Freeloader",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 485,
    duration_string: nil,
    id: 18,
    index: 8,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "Without a Song",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 353,
    duration_string: nil,
    id: 17,
    index: 7,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "Theme From M*A*S*H (Suicide Is Painless)",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 292,
    duration_string: nil,
    id: 16,
    index: 6,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "Sometime Ago",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 360,
    duration_string: nil,
    id: 15,
    index: 5,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "The Peacocks",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 239,
    duration_string: nil,
    id: 14,
    index: 4,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "We Will Meet Again (for Harry)",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 255,
    duration_string: nil,
    id: 13,
    index: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "Gary's Theme",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 337,
    duration_string: nil,
    id: 12,
    index: 2,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "You Must Believe In Spring",
    updated_at: ~N[2019-05-03 00:16:32]
  },
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 3,
    duration: 192,
    duration_string: nil,
    id: 11,
    index: 1,
    inserted_at: ~N[2019-05-03 00:16:32],
    number_of_plays: 0,
    title: "B Minor Waltz (for Ellaine)",
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.288 [debug] QUERY OK source="albums" db=0.8ms queue=0.6ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", t1."id", t1."title", t1."duration", t1."index", t1."number_of_plays", t1."inserted_at", t1."updated_at", t1."album_id"
FROM "albums" AS a0
  INNER JOIN "tracks" AS t1 ON a0."id" = t1."album_id"
WHERE (a0."id" = $1) [3]

====== ALBUM WITH EACH TRACK: [
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 489,
     duration_string: nil,
     id: 20,
     index: 10,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "All of You",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 454,
     duration_string: nil,
     id: 19,
     index: 9,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "Freddie Freeloader",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 485,
     duration_string: nil,
     id: 18,
     index: 8,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "Without a Song",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 353,
     duration_string: nil,
     id: 17,
     index: 7,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "Theme From M*A*S*H (Suicide Is Painless)",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 292,
     duration_string: nil,
     id: 16,
     index: 6,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "Sometime Ago",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 360,
     duration_string: nil,
     id: 15,
     index: 5,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "The Peacocks",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 239,
     duration_string: nil,
     id: 14,
     index: 4,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "We Will Meet Again (for Harry)",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 255,
     duration_string: nil,
     id: 13,
     index: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "Gary's Theme",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 337,
     duration_string: nil,
     id: 12,
     index: 2,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "You Must Believe In Spring",
     updated_at: ~N[2019-05-03 00:16:32]
   }},
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 192,
     duration_string: nil,
     id: 11,
     index: 1,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "B Minor Waltz (for Ellaine)",
     updated_at: ~N[2019-05-03 00:16:32]
   }}
]

11:30:08.293 [debug] QUERY OK source="albums" db=1.5ms queue=1.1ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", t1."id", t1."title", t1."duration", t1."index", t1."number_of_plays", t1."inserted_at", t1."updated_at", t1."album_id"
FROM "albums" AS a0
  INNER JOIN "tracks" AS t1 ON a0."id" = t1."album_id"
WHERE (a0."id" = $1)
  AND (t1."index" = $2) [3, 2]

====== ALBUM AND TRACK: [
  {%MusicDB.Album{
     __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
     artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
     artist_id: 2,
     genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
     id: 3,
     inserted_at: ~N[2019-05-03 00:16:32],
     title: "You Must Believe In Spring",
     tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
     updated_at: ~N[2019-05-03 00:16:32]
   },
   %MusicDB.Track{
     __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
     album: #Ecto.Association.NotLoaded<association :album is not loaded>,
     album_id: 3,
     duration: 337,
     duration_string: nil,
     id: 12,
     index: 2,
     inserted_at: ~N[2019-05-03 00:16:32],
     number_of_plays: 0,
     title: "You Must Believe In Spring",
     updated_at: ~N[2019-05-03 00:16:32]
   }}
]

11:30:08.296 [debug] QUERY OK source="albums" db=2.0ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
WHERE (a0."id" = $1) [3]

11:30:08.298 [debug] QUERY OK source="tracks" db=0.7ms queue=1.1ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id", t0."album_id"
FROM "tracks" AS t0
WHERE (t0."album_id" = $1) ORDER BY t0."album_id" [3]

====== ALL TRACKS CONTAINED BY ALBUM: [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: [
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 489,
        duration_string: nil,
        id: 20,
        index: 10,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "All of You",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 454,
        duration_string: nil,
        id: 19,
        index: 9,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "Freddie Freeloader",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 485,
        duration_string: nil,
        id: 18,
        index: 8,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "Without a Song",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 353,
        duration_string: nil,
        id: 17,
        index: 7,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "Theme From M*A*S*H (Suicide Is Painless)",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 292,
        duration_string: nil,
        id: 16,
        index: 6,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "Sometime Ago",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 360,
        duration_string: nil,
        id: 15,
        index: 5,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "The Peacocks",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 239,
        duration_string: nil,
        id: 14,
        index: 4,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "We Will Meet Again (for Harry)",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 255,
        duration_string: nil,
        id: 13,
        index: 3,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "Gary's Theme",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 337,
        duration_string: nil,
        id: 12,
        index: 2,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "You Must Believe In Spring",
        updated_at: ~N[2019-05-03 00:16:32]
      },
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 192,
        duration_string: nil,
        id: 11,
        index: 1,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "B Minor Waltz (for Ellaine)",
        updated_at: ~N[2019-05-03 00:16:32]
      }
    ],
    updated_at: ~N[2019-05-03 00:16:32]
  }
]

11:30:08.299 [debug] QUERY OK source="albums" db=0.2ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id"
FROM "albums" AS a0
WHERE (a0."id" = $1) [3]

11:30:08.299 [debug] QUERY OK source="tracks" db=0.2ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id", t0."album_id"
FROM "tracks" AS t0 WHERE (t0."index" = $1) AND (t0."album_id" = $2)
ORDER BY t0."album_id" [2, 3]

====== ONE TRACK CONTAINED BY ALBUM: [
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2019-05-03 00:16:32],
    title: "You Must Believe In Spring",
    tracks: [
      %MusicDB.Track{
        __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
        album: #Ecto.Association.NotLoaded<association :album is not loaded>,
        album_id: 3,
        duration: 337,
        duration_string: nil,
        id: 12,
        index: 2,
        inserted_at: ~N[2019-05-03 00:16:32],
        number_of_plays: 0,
        title: "You Must Believe In Spring",
        updated_at: ~N[2019-05-03 00:16:32]
      }
    ],
    updated_at: ~N[2019-05-03 00:16:32]
  }
]
:ok
$ 

1 Like