Generate selects dynamically from DSL

I want to take a DSL like this:

    [PATIENT_LAST_NAME:  [field: [appointment: :last_name]]],
    [PATIENT_SEX:  [lookup: [appointment: :sex]]],
    [PATIENT_STREET_ADDR_LINE1:  [:patient_addr1]],
    [PATIENT_STREET_ADDR_LINE2:  [string: ""]],

and generate a select clause for each row in a query (this is for reporting).

I’ve tried a variety of things, but how to achieve any of them is stymied if nothing else by the lack of adequate documentation about dynamic selects.

I tried this:

  def query do
    q = from(
      t in TestResult,
…
    where:
…    select: %{
      id: t.id,
      _address: a.address},
    select_merge: ^select_clauses(dsl)
    )
  end

with the expectation that I would write select_clauses which do a reduce on the lines of the DSL into a map, like:

def select_clauses(dsl) do
dsl
|> Enum.reduce(
      %{},
      fn [dsl_row, selects] ->
        selects |> Map.merge(generate_select_clause(dsl_row))
      end
    )
end

  def generate_select_clause({col_name, {:field, {from_table, fld}}}) do
    dynamic( [{^from_table, t}],
      ^%{t[col_name] => t[fld]}
    )
  end

  def generate_select_clause({col_name, {:lookup, …

I’m getting several errors on my generate_select_clause here:

(Ecto.Query.CompileError) %{col_name => lookup_clause(fld, from_table, dynamic)} is not a valid query expression.

variable “from_table” is unused (if the variable is not meant to be used, prefix it with an underscore)

variable “t” does not exist and is being expanded to “t()”, please use parentheses to remove the ambiguity or change the variable name

There is something going on with scope or some damn thing here that I just don’t understand and can’t find documented or discussed anywhere.

Is my general approach reasonable? What is going on with my generate_select_clause?

TIA

Are there multiple tables or just the one? In your query you just have one and if that’s the case you don’t need to use a name for the table in your dynamic. You can just do dynamic([t] ..., though I have not tried to use a dynamic within a select :man_shrugging:. Do you need to use Ecto.Query.API.map() in the select_merge?

I do it like do:

query
|> select([a, _t], map(a, ^a_fields))
|> select_merge([_a, t], map(t, ^t_fields))

Multiple tables, joined.

we can create a query using a Domain Specific … To select the fields that you want in your query, you use the select method.