Easily build composable queries for Ecto.
Adding Inquisitor to a project is simple:
defmodule MyApp.PostController do use Inquisitor def index(conn, params) do posts = App.Post |> build_query(conn, params) |> Repo.all() json(conn, posts) end end
build_query/3 is added to
MyApp.PostController. It takes a queryable variable, the
conn, and the
params as arguments.
This sets up a key/value queryable API for the
Post model. Any
combination of fields on the model can be queried against. For example,
[GET] /posts?foo=bar&baz=qux could create the query:
SELECT p0."foo", p0."baz" FROM posts as p0 WHERE (p0."foo" = $1) AND (p0."baz" = $1);
$2 will get the values of
By default, Inquisitor is an opt-in library. It will not provide any querying access to any key/value pair. The params list will be iterated over and a no-op function is called on each element. You must add custom query handlers that have a higher matching order on a case by case basis.
If you'd like to add a catch-all for any key/value pair you can override the default:
def build_query(query, attr, value, _conn) do Ecto.Query.where(query, [r], field(r, ^String.to_existing_atom(attr)) == ^value) end
However, this is not recommended. Instead you should create a
@whitelist modulue attribute that contains all of they keys you
allowing access to:
@whitelist ["title", "bio"] def build_query(query, attr, value, _conn) when attr in @whitelist do Ecto.Query.where(query, [r], field(r, ^String.to_existing_atom(attr)) == ^value) end
This will handle matching keys in the whitelist and all unmatched keys will fallback to the pass-through without affecting the query.
Adding custom query handlers
build_query/4 to add key/value pair handlers:
defmodule MyApp.PostsController do use Inquisitor def index(conn, params) do posts = App.Post |> build_query(params) |> Repo.all() json(conn, posts) end def build_query(query, "inserted_at", date, _conn) do Ecto.Query.where(query, [p], p.inserted_at >= ^date) end end
Handing fields that don't exist on the model
The keys you query against don't need to exist on the model. Revisiting the date example, let's say we want to find all posts inserted for a given month and year:
def build_query(query, attr, value, _conn) when attr == "month" or attr == "year" do Ecto.Query.where(query, [e], fragment("date_part(?, ?) = ?", ^attr, e.inserted_at, type(^value, :integer))) end
Usage Outside of Phoenix Controllers
To use inside a module other than a Phoenix Controller, you'll need to import
Ecto.from/1 otherwise you may see an error like
cannot use ^value outside of match clauses.
Note: we use
warn: false to suppress an incorrect warning generated by Elixir thinking
from is unused.
defmodule MyApp.PlainModule do import Ecto.Query, only: [from: 1], warn: false use Inquisitor end
We collect all Inquisitor plugins that extend its behavior:
- inquisitor_jsonapi - implements query handling according to the JSON API spec
This library follows Semantic Versioning
Want to help?
Please do! We are always looking to improve this library. Please see our Contribution Guidelines on how to properly submit issues and pull requests.
DockYard, Inc. © 2016