Current section

Files

Jump to
inquisitor lib inquisitor.ex
Raw

lib/inquisitor.ex

defmodule Inquisitor do
@moduledoc """
Composable query builder for Ecto.
Inquisitor provides functionality to build a powerful query builder
from a very simple pattern.
defmodule App.PostController do
use Inquisitor
end
This will inject the necessary code into the `App.PostController`
module. The function name depends upon the schema name. In this case
the function injected will be called `build_query/1`.
The builder function expects a flat map of key/value pairs to be
passed in. Typically this might be captured from an incoming request.
`[GET] /posts?foo=hello&bar=world` may result in a params map of
`%{"foo" => "hello", "bar" => "world"}`
def index(conn, params) do
posts =
build_query(params)
|> Repo.all()
json(conn, posts)
end
## Writing custom query handlers
Custom query handlers are written using `defquery/2`. This macro
has the `query` variable injected at compile-time so you can use it
to build up a new query. The result of the this macro should always
be the query. The first argument will be the param key to match on,
the second is the value matcher:
defquery "title", title do
query
|> Ecto.Query.where([r], r.title == ^title)
end
This macro will inject a new function at compile time. The above example
will produce:
def build_query(query, [{"title", title} | tail]) do
query
|> Ecto.Qiery.where([r], r.title == ^title)
|> build_query(tail)
end
The macro is there for convenience. If you'd like to just write the function
and avoid the macro you are free to do so.
"""
defmacro __using__(_opts) do
quote do
import Inquisitor
@before_compile Inquisitor
end
end
defmacro __before_compile__(_env) do
quote do
def build_query(query, params) when is_map(params) do
params = Map.to_list(params)
build_query(query, params)
end
def build_query(query, []), do: query
def build_query(query, [{_attr, _value}|tail]) do
build_query(query, tail)
end
defoverridable [build_query: 2]
end
end
defmacro defquery(key, value, [do: do_expr]) do
do_expr = Macro.prewalk(do_expr, fn
{:query, meta, nil} -> {:query, meta, __MODULE__}
node -> node
end)
[value, when_expr] = case value do
{:when, _meta, expr} -> expr
value -> [value, true]
end
quote do
def build_query(query, [{unquote(key), unquote(value)} | tail]) when unquote(when_expr) do
unquote(do_expr)
|> build_query(tail)
end
end
end
end