Ecto select either a couple of fields or all

Often in my contexts (Phoenix) I have some kind of list selection where I want to allow the caller of the function to specify the preloads and the select fields. The preloads are easy, as these can just be passed to Repo.preload/2, but I’m kind of struggling with the select.

  def get_company_addresses_by_uuid(uuids, opts) when is_list(uuids) do
    preload = Keyword.get(opts, :preload, [])
    fields = Keyword.get(opts, :fields, nil)

    query =
      |> ProjectName.Query.dynamic_select(fields)
      |> where([ca], ca.uuid in ^uuids)

    |> Repo.all()
    |> Repo.preload(preload)

And dynamic_select:

  def dynamic_select(query, nil), do: query |> select([q], q)
  def dynamic_select(query, []), do: query |> select([q], q)
  def dynamic_select(query, fields) when is_list(fields), do: query |> select([q], ^fields)
  def dynamic_select(query, _), do: query

This feels like something Ecto should already be able to do, but I just haven’t found the right way. I’ve tried:

from(ca in CompanyAddress, where: ca.uuid in ^uuids, select: map(ca, fields)

Fails when fields is nil, [], or just generally an unexpected value.

Is there a better way of doing this?

1 Like