Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composing an Ecto query with multiple columns in a where clause joined by OR

Tags:

elixir

ecto

Let's say I have this query:

User
|> where([u], u.name == "John" or u.age == 24)

I want to turn this into a function that can take a keyword list of field names and field values and dynamically generate this same query. The function definition would look something like this:

def where_any(query, field_names_to_values) do
  ...
end

Is this possible with Elixir and Ecto?

like image 673
Josh Rieken Avatar asked Jan 26 '16 08:01

Josh Rieken


People also ask

Is it possible to join on interpolated ecto queries?

Currently it is possible to join on: an interpolated Ecto query with zero or more where clauses, such as c in ^ (from "posts", where: [public: true]) a query fragment, such as c in fragment ("SOME COMPLEX QUERY") , see "Joining with fragments" below.

What is a composable ecto query?

Ecto queries are composable. For example, the query above can actually be defined in two parts: Composing queries uses the same syntax as creating a query. The difference is that, instead of passing a schema like User on the right-hand side of in, we passed the query itself.

What are the different types of ecto queries?

Ecto queries come in two flavors: keyword-based and macro-based. Most examples will use the keyword-based syntax, the macro one will be explored in later sections. Let's see a sample query: In the example above, we are directly querying the "users" table from the database. Ecto allows a limited set of expressions inside queries.

Does ecto query support fragments in joins?

When you need to join on a complex query, Ecto supports fragments in joins: Although using fragments in joins is discouraged in favor of Ecto Query syntax, they are necessary when writing lateral joins as lateral joins require a subquery that refer to previous bindings:


Video Answer


1 Answers

Wouldn't normally advocate a macro, but ecto does so much complex magic I think it's best in this case to keep the abstract syntax tree intact.

defmacro any_expr(var, [{key, value} | rest]) do
  Enum.reduce(
    rest,
    quote do unquote(var).unquote(key) == unquote(value) end,
    fn ({k, v}, acc) ->
      quote do: unquote(acc) or unquote(var).unquote(k) == unquote(v)
    end)
end

should work like this:

User
|> where([u], any_expr(u, [name: "John", age: 24]))

(note that this is untested code...)

like image 108
jisaacstone Avatar answered Oct 22 '22 19:10

jisaacstone