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

  defp build_post_query(query, [{"inserted_at", date}|tail]) do
    query
    |> Ecto.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
defp build_event_query(query, [{attr, value}|tail]) when attr == "month" or attr == "year" do
  query
  |> Ecto.Query.where([e], fragment("date_part(?, ?) = ?", ^attr, e.inserted_at, type(^value, :integer)))
  |> build_event_query(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
defp 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)