Is it possible to call a function within a quoted fragment?

Our application provides a macro to help with full text search. The macro, as it exists currently looks like this:

defmodule MyApp.Search
  defmacro tsquery(column, terms, language \\ "english") do
    quote do
      fragment("to_tsvector(?, ?) @@ to_tsquery(?, ?)",
               unquote(language),
               unquote(column),
               unquote(language),
               unquote(terms))
    end
  end
end

The Search module also implements a function for sanitizing and joining the search terms. We’re adding the ability to control whether words are “anded” or “ored” together, and we’d like to do that within the tsquery macro directly. Functionally, what we’re looking to do looks like this:

defmodule MyApp.Search
  defmacro tsquery(column, terms, operator, language \\ "english") do
    quote do
      fragment("to_tsvector(?, ?) @@ to_tsquery(?, ?)",
               unquote(language),
               unquote(column),
               unquote(language),
               unquote(ts_query_text(terms, operator)))
    end
  end
end

This fails because ts_query_text is called at compile time. None of my various attempts at restructuring the macro to call the function within the quote block and the fragment macro have worked.

Is it possible to call a function from inside the quoted fragment macro?

Why can’t you call this function before calling MyApp.Search.tsquery?

MyApp.Search.tsquery(column, ts_query_text(terms, operator), operator, language)

It’d be easier to understand for the next person reading this code …

That is what we’re doing currently. Honestly I’m fine with that solution, but I’m wondering if this is possible.

Replace with:

               unquote(quote(do: ts_query_text(terms, operator))))

Then it becomes conceptually the same as calling it like:

MyApp.Search.tsquery(column, ts_query_text(terms, operator), operator, language)
1 Like

It seemed promising, but it ends up raising:

== Compilation error in file lib/queries/search_query.ex ==
** (Ecto.Query.CompileError) `ts_query_text(terms, operator)` is not a valid query expression.

* If you intended to call a database function, please check the documentation
  for Ecto.Query to see the supported database expressions

* If you intended to call an Elixir function or introduce a value,
  you need to explicitly interpolate it with ^

Oh sorry, forgot the ^, this:

               ^unquote(quote(do: ts_query_text(terms, operator))))

^.^;

2 Likes