Ecto β€” select field or nil dynamically

How to dynamically or conditionally select a value from a binding or nil?

def fetch_stuff(query, include_some_value?) do
  # SomeModel embeds SomeOtherModel which contains lots of stuff.
  # SomeModel.embedded is not required (default nil).
  preferably_nil_sometimes = (
    # binding.id (:integer) works but nil would be much better.
    if include_some_value?, do: :lots_of_stuff, else: :id
  )

  from [binding] in query,
  select: %SomeModel{
    id: binding.id,
    embedded: field(binding, ^preferably_nil_sometimes)
  }
end
def fetch_stuff(query, false = _include_some_value?) do
  from(query, select_merge: %{embedded: nil})
end

def fetch_stuff(query, true = _include_some_value?) do
  # I'm not quite following if the query should be returned unchanged
  # like this or if there is other "stuff" to be done
  query
end

Thank you.

Actually SomeModel is also embedded and the source (binding) is not of the same type (schema).
Select_merge will complain about an incompatible merge.

those select expressions are incompatible. You can only select_merge:

  * a source (such as post) with another source (of the same type)
  * a source (such as post) with a map
  * a struct with a map
  * a map with a map

Also select_merge will delete any field not present in a map.
In this example SomeModel will not have an id because it will be deleted.

from [binding] in query,
select: %ParentModel{
  stuff: binding.parent_stuff,
  other_stuff: %SomeModel{
    id: binding.id,  # <-- `select_merge: %{embedded: nil}` will delete all but :embedded
    embedded: field(binding, ^preferably_nil_sometimes)
  }
}

The query should set :embedded to nil or to binding.lots_of_stuff.

Take a look at

Thank you, I have.

Using dynamic I’m not able to get the result I’m looking for.
I would like to select (embedded) structs not maps.
And I’d like to dynamically set only a single field, not an entire struct (or map).

Have you tried using struct/2, perhaps combined with select_merge?

1 Like

Struct/2 wants to infer the type (model/schema) from the source (binding).
I’m composing β€œcustom” models using data from other sources.
So I guess that would require struct/3 which would be awesome :slight_smile: