# `DDD.Filter`
[🔗](https://github.com/blackuprise/vicander/blob/v0.1.1/lib/ddd/use_cases/filter.ex#L1)

A small, safe filter language that becomes a SQL `WHERE`, for the listing functions of
`DDD.Repository.Ecto` adapters and `DDD.Projection`s (`list/1`, `page/1`, `slice/1`,
`count/1`).

    [owner_id: id, status: :open]                       # all of them (AND)
    [open_count: {:gt, 3}]                              # a comparison
    {:or, [[status: :open], [title: {:like, "Buy%"}]]}
    {:not, [status: :done]}
    [{:spec, MyApp.Specs.Overdue}]                      # a specification, see below

Forms (`field` is an atom, `value` is in its domain form: atoms, `Date`s, ids as strings):

| form | meaning |
|---|---|
| `{field, value}` | equal (`nil` means `IS NULL`) |
| `{field, {op, value}}` or `{field, op, value}` | `op` is `:eq`, `:neq`, `:gt`, `:gte`, `:lt`, `:lte`, `:in`, `:not_in`, `:like`, `:ilike` |
| `[filter, ...]` or a keyword list or a map | every one of them |
| `{:and, [filter]}`, `{:or, [filter]}`, `{:not, filter}` | combinators (so `:and`, `:or`, `:not` can't be field names here) |
| `{:spec, spec}` | a `DDD.Specification` translated by `DDD.Specification.to_filter/2` |

Values are never interpolated into SQL: they are bound parameters, converted with
`DDD.Type.dump/2` the same way `find_by_*` does. An unknown field, an unknown operator or a
malformed expression is a business error with code `:invalid_filter` (so a filter built from a
client's request can be answered with it), never a crash or an injection.

# `build`

```elixir
@spec build(term(), %{required(atom()) =&gt; term()}) ::
  {:ok, Ecto.Query.dynamic_expr()} | {:error, DDD.Error.t()}
```

Build the `Ecto.Query.dynamic/2` for `filter`. `types` maps each field to its domain type
(`%{title: :string, ...}`); only those fields can be filtered on.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
