README.md

# Inquisitor #

Easily build composable queries for Ecto.

[![Build Status](https://secure.travis-ci.org/dockyard/inquisitor.svg?branch=master)](http://travis-ci.org/dockyard/inquisitor)

## Usage ##

Adding Inquisitor to a project is simple:

```elixir
defmodule MyApp.PostController do
  use Inquisitor, with: MyApp.Post

  def index(conn, params) do
    posts =
      build_post_query(params)
      |> Repo.all()

    json(conn, posts)
  end
end
```

After `use Inquisitor, with: MyApp.Post` a custom function is added to
the `MyApp.PostController`. In this case that function is
`build_post_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:

```sql
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"`,

### Security ###

Inquisitor will allow any key/value pair to be queried against, you will
likely want to only allow access to a limited number of fields. To do
this you can `whitelist` any number of fields that can be allowed to
queried:

```elixir
use Inquisitor, with: MyApp.Post, whitelist: ["title", "body"]
```

### 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:

```elixir
defmodule MyApp.PostsController do
  use Inquisitor, with: MyApp.Post

  def index(conn, params) do
    posts =
      build_post_query(params)
      |> Repo.all()

    json(conn, posts)
  end

  def build_post_query(query, [{"inserted_at", date}|tail]) do
    query
    |> where([p], p.inserted_at >= ^date)
    |> build_post_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:

```elixir
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 passed on for handling. So even if the params come as:

```elixir
%{ "foo" => "true" }
```

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

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

## Authors ##

* [Brian Cardarella](http://twitter.com/bcardarella)

[We are very thankful for the many contributors](https://github.com/dockyard/inquisitor/graphs/contributors)

## Versioning ##

This library follows [Semantic Versioning](http://semver.org)

## Want to help? ##

Please do! We are always looking to improve this library. Please see our
[Contribution Guidelines](https://github.com/dockyard/inquisitor/blob/master/CONTRIBUTING.md)
on how to properly submit issues and pull requests.

## Legal ##

[DockYard](http://dockyard.com/), Inc. © 2016

[@dockyard](http://twitter.com/dockyard)

[Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php)