How to select_merge a dynamic binding into a map with variable keys?


Given a query with N joins at runtime

query = from(e in Entity)

assocs = [:assoc_a, :assoc_b, :assoc_c, ...] # list of unknown length

query = assocs
|> Enum.reduce(query, fn assoc_name, acc_query -> 
  join(acc_query, :left, [e], assoc(e, ^assoc_name), as: ^assoc_name)

Is it possible to map the entire joined relation back onto the return value using select_merge like so?

|> Enum.reduce(query, fn assoc_name, acc_query -> 
  select_merge(acc_query, [{^assoc_name, bind}], %{^assoc_name => bind})

If not, is there any way to construct a select statement that returns all of the associations at runtime, without prior knowledge of the total number of joined relations?

The project I’m working on involves a dynamic query constructor core built using macros and my feeling is that we are essentially re-inventing preloads… But I’d like to avoid refactoring our codebase if at all possible since the number of tables we have to touch is quite high

Hello and welcome!

Why not just use preloads? You should just be able to pass your initial assoc list to Repo.preload.

Yes preload would definitely be the sensical option haha

The project was just written in a way where the select statement is generated, and there is a bunch of complexity around how the results are then patched back onto the data in a different layer

Basically we should probably use preload, but that would have a significant time cost for refactoring, and I’m curios if there’s anyway to avoid that haha

Is it not possible to use select_merge and/or dynamic statements in that way?

Have you tried map? I do similar but I flatten them all into the one map. It looks like you can handle preloads from the docs.

1 Like

Hey thanks for the suggestions @sodapopcan and @cmo

Ultimately I bit the bullet and and refactored the code base to leverage Ecto properly.

@cmo I had looked at map previously but it’s not quite the right fit, unless I’m mistaken we can only use it to pull fields out of a binding, as opposed to set a binding to a new field.

Thank you both for your suggestions!