Difficulty trying to use Macros to compose ecto query

Hi every one!
I’m a bit lost trying to compose a queries using macro.

defmacrop to_query(params, append_to_query) do 
    quote do
        unquote(params)
        |> case do
            {:order, value} -> 
                from q in unquote(append_to_query), order_by: [{^value, :id}]
            {key, value} when is_bitstring(value) -> 
                from q in unquote(append_to_query), where: like(q ? key, ^"%#{value}%")
            {key, value} -> 
                from q in unquote(append_to_query), where: q ? key == ^value
            _ ->
                unquote(append_to_query)
        end
    end
end

@spec compose(Ecto.Schema.t(), Keyword.t) :: Ecto.Query.t()
def compose(schema, params) do
    with true <- Code.ensure_compiled?(schema) do
        Enum.reduce(params, (from q in schema), 
                        fn attrs, acc -> to_query(attrs, acc) end)
      else
        _ -> raise ArgumentError, message: "Invalid schema #{schema}"
    end
end

As you can see in that line with <q ? key>, I need to call unquoted key value from q in order to compare. As I can’t do: q.unquote(key) or q.(unquote(key)) because its not a function. Also was thinking to get to code from string like : "q.#{unquote(key) " and then expand to code, but didn’t found any examples.
Some one know how it can be done?
I checked Macro documentation it is still black magic to me :joy: but I want to learn it even more!

Thank you! :crossed_fingers:

Ok I was doing it wrong! Don’t need macro here. Ecto is enough dinamic :sweat_smile: just use with bindingless operations:

    @spec compose(Ecto.Schema.t(), Keyword.t) :: Ecto.Query.t()
    def compose(schema, params) do
        Enum.reduce(params, (from q in schema), 
                            fn attrs, acc -> to_query(attrs, acc) end)
    end

    defp to_query({:order, value}, acc), do: from q in acc, order_by: [{^value, :id}]
    defp to_query(params, acc), do: from q in acc, where: ^[params]
    defp to_query(_, acc), do: acc

and ilike queries can be done using fragments :man_facepalming:

This should basically never need to be called at runtime, what’s the overall goal here?

Yeah you right I forgot to remove that part of code. But this question let me to think suppose we got this kind of code:

defmacro __using__(schema: schema) do
quote do
  @doc """
  Check if schema is a valide module or raise un error
  """
  defmacro run?(name, expression) do
    quote do
      with true <- Code.ensure_compiled?(unquote(name)) do
        #do some bad stuff here
        ...
      else
        _ -> raise ArgumentError, message: "Invalid module name #{unquote(name)}"
      end
    end
  end

Are there any better way to be sure that passed option “schema” is an valid module? Because I was thinking if module name are just registered atoms then any atoms can passed like params to be then executed.