I’m trying to build out a function that, if desired, I can build a list of joins
in Ecto when I want to use preload
to save trips to the database. If I know ahead of time what the joining keys will be and they’re all the same, the following works just fine:
def join_for_preload(params, base_query, preloads), do: {params, build_joins(query, preloads)}
defp build_joins(query, []), do: query
defp build_joins(query, [{nested_query, nested_preloads} | preloads] do
nested_queryable = query.__schema__(:association, nested_query).queryable
sub_query = build_joins(nested_queryable, nested_preloads)
query
|> join(:left, [q], s in subquery(sub_query), on: [id: q.known_id])
|> build_joins(preloads)
defp build_joins(query, [preload | preload]) do
query
|> join(:left, [q], a in assoc(q, ^preload)
|> build_joins(preloads)
Where preloads
takes on the structure of the :preload
option e.g. [parent: [child: :field]]
or [:field1, :field2, etc...]
This causes problems when I don’t know what the parent field is called, so I’d like to alter the function that handles nested associations to do something like this:
defp build_joins(query, [{nested_query, nested_preloads} | preloads] do
nested_query_struct = query.__schema__(:association, nested_query)
[nested_queryable, nested_owner_key] = [nested_query_struct.queryable, nested_query_struct.owner_key]
sub_query = build_joins(nested_queryable, nested_preloads)
query
|> join(:left, [q], s in subquery(sub_query), on: [id: q[^nested_owner_key]]) # <-- Use a dynamic key value here
|> build_joins(preloads)
Questions:
Is this possible with Ecto or a macro?
Is it worth the effort? Or should I just write the joins if/when I need them?