Using my own function in "where(...)" ---> is not a valid query expression

I have this query which works:

  import Ecto.Query

  where(Article, [a], x.status == "active")

Whereas this doesn’t:

  # 1
  where(Article, [a], Article.active?(a))

  # 2
  where(Article, [a], Article.active?(a) == true)

Error:

Article.active?(x) is not a valid query expression.

active? defined as:

      def active?(a) do
        a.status == "active"
      end

How to make it work?

Ecto allows a limited set of expressions inside queries. […]
You can find the full list of operations in Ecto.Query.API .

Ecto.Query.API

Lists all functions allowed in the query API.

This means: you can’t.

4 Likes

You can make it a macro.

defmacro active?(a) do
  quote do: unquote(a).status == "active"
end
3 Likes

I saw this post and I tried to use a macro that returns a string combining the value extracted from a structure and something else but it didn’t work, something like:

defmacro return_string(some_struct) do
  quote do: "#id-{unquote(some_struct).id}"
end

It’s a bit weird because the returning value is a string but for some reason the compiler checks that is not right. My guess is that the interpolation happens after the evaluation and the compiler is unhappy because it doesn’t know what to expect. Just a guess!

Macro or not, I don’t believe that syntax works in Ecto. some_struct is going to be a compile-time reference to a table, not a struct.

You’d need to rewrite the expression to something database-friendly like:

fragment("'#id-' || ?", some_table.id)

(which you could wrap into a macro like in earlier posts). The || here is not Elixir’s version, but Postgres’s.

2 Likes

Right! It’s a reference to a table not to an struct. Thanks for pointing that out. I tested using a fragment as you suggested and it worked great. Thanks for your comment @al2o3cr.