Let’s A, B and C be three schemas with A having one-to-many assocs :bs and :cs towards B and C (A has many Bs and Cs, and B and C belongs to one A).
What do you use to do to preload both assocs?
First option might be:
query = from(a in A, preload: [:bs, :cs])
This code will generate 3 SQL queries, efficiency depends on the number of A rows and the length of the query strings depends also on the number of A rows.
Second option might be
query = from(a in A, left_join: b in assoc(a, :bs), left_join: c in assoc(a, :cs), preload: [bs: b, cs: c])
I this case we get just one SQL query, but the efficiency will depends on the product of B rows x C rows since the SQL query contains two left joins that are unrelated (efficiency has nothing to with indexes):
SELECT a..., b..., c... FROM "as" AS a LEFT JOIN "bs" AS b ON b.a_id = a.id LEFT JOIN "cs" AS c ON c.a_id = a.id
I think none of the options are acceptable so I am fetching entries for two queries (introducing the same :order_by) and then I am merging the preloads with a function add_preload (implemented with Enum.zip):
q1 = from(a in A, left_join: b in assoc(a, :bs), preload: [bs: b])
q2 = from(a in A, left_join: c in assoc(a, :cs), preload: [cs: c])
Repo.all(q1) |> add_preload(Repo.all(q2), :cs)
I wrote a macro that takes a query (from a in A) and the names of the assocs and expand the code to the previous pattern but the problem with this approach is that it is not very composable with other preloads (nested ones in particular).
What do you use to do in these cases? Am I missing anything magical in Ecto?