Inquisitor

Easily build composable queries for Ecto.

Build Status

Usage

Adding Inquisitor to a project is simple:

defmodule MyApp.PostController do
use Inquisitor, with: MyApp.Post
def index(conn, params) do
events =
build_event_query(params)
|> Repo.all()
json(conn, events)
end
end

After use Inquisitor, with: MyApp.Post a cutom function is added to the MyApp.PostController. In this case that function is build_event_query. The name of the function is dynamically created based upon the model name. So if the model was MyApp.FooBarBaz the corresponding function would be build_foo_bar_baz_query.

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, requesting [GET] /posts?foo=bar&baz=qux will create the query:

SELECT p0."foo", p0."baz" FROM posts as p0 WHERE (p0."foo" = $1) AND (p0."baz" = $1);

$1 and $2 will get the values of "bar" and "qux",

Adding custom query handlers

Simple key/value matching is not always what you want. In that case you can define custom handlers for certain keys. Let's say you want to query based upon inserted_at values, querying for all values that came on and after that date:

defmodule MyApp.PostsController do
use Inquisitor, with: MyApp.Post
def index(conn, params) do
events =
build_event_query(params)
|> Repo.all()
json(conn, events)
end
def build_event_query(query, [{"inserted_at", date}|tail]) do
query
|> where([p], p.inserted_at >= ^date)
|> build_user_query(tail)
end
end

The query is built recursively by iterating over all the params. If there is a matching custom handler, it uses that otherwise defaults to the key/value handler.

Handing fields that don't exist on the model

The keys you query against don't need to exist on the model. Revisting the date example, let's say we want to find all posts inserted for a given month and year:

def build_event_query(query, [{attr, value}|tail]) when attr == "month" or attr == "year" do
query
|> where(fragment("date_part(?, ?) = ?", ^attr, e.inserted_at, type(^value, :integer)))
|> build_event_query(q, tail)
end

That's it!

Built in handlers

There are a few built in handlers

Booleans

Booleans that come in as text will be typecast to an actual boolean type then past on for handling. So even if the params come as:

%{ "foo" => "true" }

You will want to pattern match on the actual boolean value:

def build_event_query(query, [{"foo", true}|tail]) do
...

Authors

We are very thankful for the many contributors

Versioning

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

@dockyard

Licensed under the MIT license