How to create a query based on an atom key

Let’s say my Model has a few bools ([:is_a, :is_b, :is_c]). I was to query based on an input ([“a”, “b”, “c”])

something like this:


def find_something(type) do
  key = "is_#{type}" |> String.to_atom
  from(m in Model, where: ^key == true)
end

This doesn’t work since ^key resolves to :is_xxx, but I want m.is_xxx. How can I achieve this? get_by/2 doesnt work since it will return multiple values.

from(m in Model, where: field(key) === true)

You usually do not want to use String.to_atom/1, its documentation contains this warning:

Warning: this function creates atoms dynamically and atoms are not garbage-collected. Therefore, string should not be an untrusted value, such as input received from a socket or during a web request. Consider using to_existing_atom/1 instead.

In my opinion, you should create a separate function head for each allowed input and map it to the field manually:

def find_something("a"), do: from(m in Model, where m.is_a == true)
def find_something("b"), do: from(m in Model, where m.is_b == true)
def find_something("c"), do: from(m in Model, where m.is_c == true)
1 Like

Or you can do something like this to prevent repetition:

def find_something(type) when type in ["a", "b", "c"] do
  key = "is_#{type}" |> String.to_atom
  from(m in Model, where: field(key) === true)
end

This way, you can be sure, you will be creating only a fixed set of atoms.

1 Like