Search code examples
sqlelixirecto

Dynamic Elixir Ecto Query


I have a list of fields, and I want to take that list and dynamically create an Ecto query from it.

I think I can use Enum.reduce, but I'm not sure how to get the pieces to work together.

Each field in the list would use an ilike and be cast to a text, something like:

def lookup(search_term) do
  fields = [:id, :title, :body]

  query =
    from p in Post,
    where: build_query(fields, search_term)
    select: p

  Repo.all(query)
end

def build_query(fields, search_term) do
  Enum.reduce(fields, fn(field) ->
    fragment("CAST(? AS text) ILIKE ?", field(t, ^field), ^search_term))
  end)
end

Solution

  • is built with macros and from/2 does actually expand its whole arguments.

    One might define their own macros as described here but in your particular case, it’s enough to compose a query.

    To compose a query, one reduces a query object on additional ‘actions’.

    The code below should work

      @fields ~w|title body|a
    
      def lookup(search_term) do
        query = from p in Post # prepare the accumulator
        # and then reduce the query to compose
        query = build_query(query, @fields, search_term)
        # add additional tuning
        query = from p in query, select: p.id
    
        Repo.all(query)
      end
    
      def build_query(query, fields, search_term) do
        Enum.reduce(fields, query, fn field, query ->
          from q in query,
          where: fragment("CAST(? AS text) ILIKE ?", field(q, ^field), ^search_term)
        end)
      end