Search code examples
joinormdependencieselixirecto

Elixir Ecto : multiple joins and reusable/composable queries


In Ecto you can make reusable/composable queries like so :

defmodule AModel
  #...
    
  def anonymous(q), do: q |> where([am], is_null(am.user_id))
  end
end

See more examples on this blog post.

However, I face a problem using multiple joins.

Let's suppose we have a schema that looks like this :

  • AModel belongs to BModel
  • BModel belongs to CModel
  • CModel belongs to DModel

The solution proposed in this article does not really work with deep joins :

q = DModel
  |> join(:inner, [dm], cm in assoc(dm, :c_models))
  |> join(:inner, [_, cm], bm in assoc(cm, :b_models))
  |> join(:inner, [_, _, bm], am in assoc(bm, :a_models)) 
  |> AModel.anonymous

Querying functions take as first (second for join) argument a binding table. It contains the previous joins and is sadly tight to join order.

In our case, the anonymous function target the starting table. However in the query example, AModel is the 4th binding ...

Any idea or technic to get rid of this order dependency ?

EDIT :

I get an answer from the blog author. He told me that there is no native other way to handle bindings than by position in the table. He also gave this article highlighting this fact.

But for god sake, if order only matter, why I can't create above it a naming mapping that associates name with binding index ?

Is this too much to ask :p ?


Solution

  • Use named bindings

    Ecto 3.0 added named bindings for this use case.