Skip to content

Commit

Permalink
Merge pull request #22 from valyukov/custom-predicates
Browse files Browse the repository at this point in the history
Custom predicates
  • Loading branch information
albertored committed Jul 1, 2020
2 parents 439f8b1 + 955cb6e commit cdf2b4a
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 175 deletions.
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"mix.exs"
],
line_length: 120,
import_deps: [:ecto]
import_deps: [:ecto, :ecto_sql]
]
161 changes: 3 additions & 158 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ end
## Nice to have

- Add advanced search documentation
- Custom query function (fragments or custom query functions)
- Configure predicate aliases
- Demo project

## Ecto internals currently used
Expand Down Expand Up @@ -58,163 +58,8 @@ end

### Examples

In the following we assume these schemas are defined in your application:

```elixir
defmodule MyApp.Post do
use Ecto.Schema

schema "posts" do
has_many :comments, MyApp.Comment

field :title
field :body
field :published, :boolean

timestamps()
end
end
```

```elixir
defmodule MyApp.Comment do
use Ecto.Schema

schema "comments" do
belongs_to :post, MyApp.Post

field :body

timestamps()
end
end
```

#### Simple query

Given this json representation of the query

```json
{
"m": "or",
"id_in": [1, 2],
"title_and_body_cont": "text",
"comments_body_eq": "body",
"s": ["title desc", "inserted_at asc"]
}

```

the following SQL query is sent to the database

```sql
SELECT posts.* FROM posts INNER JOIN comments ON posts.id = comments.post_id \
WHERE posts.id IN (1, 2) \
OR (posts.title ILIKE '%text%' AND posts.body ILIKE '%text%') \
OR comments.body == "body" \
ORDER BY posts.title DESC, posts.inserted_at ASC;
```

#### Grouping queries

Query fields can be nested for obtaining more advanced filters.

Given this json representation of the query

```json
{
"m": "and",
"id_in": [1, 2],
"g": [
{
"m": "or",
"c": {
"title_and_body_cont": "text",
"comments_body_eq": "body"
}
}
],
"s": ["title desc", "inserted_at asc"]
}

```

the following SQL query is sent to the database

```sql
SELECT posts.* FROM posts INNER JOIN comments ON posts.id = comments.post_id \
WHERE posts.id IN (1, 2) \
AND ( \
(posts.title ILIKE '%text%' AND posts.body ILIKE '%text%') \
OR comments.body == "body") \
ORDER BY posts.title DESC, posts.inserted_at ASC;
```

### Supported predicates

#### Base predicates
```
eq
not_eq
cont
not_cont
lt
lteq
gt
gteq
in
not_in
matches
does_not_match
start
not_start
end
not_end
true
not_true
false
not_false
present
blank
null
not_null
```

#### Composite predicates
```
eq_any
not_eq_all
cont_all
cont_any
not_cont_all
not_cont_any
matches_all
matches_any
does_not_match_all
does_not_match_any
start_any
not_start_all
end_any
not_end_all
```

#### Combinators
```
or
and
```

You can read more about predicates on [ransack wiki page](https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching).

#### Note

`LIKE` queries can suffer of [LIKE injection](https://github.blog/2015-11-03-like-injection/) attacks.

For this reason all predicates which result in a `LIKE` query (`cont`, `not_cont`, `start`, `not_start`, `end`, `not_end`
and their composite predicates) are properly escaped.

Some exceptions are `matches`, `does_not_match` and their composite predicates that allows `%`, `_` and `\` chars in the value.
You should be very careful when allowing an external user to use these predicates.
For more details on the used query language and query examples see the
[official documentation](https://hexdocs.pm/ex_sieve/ExSieve.html#module-examples).

## Contributing

Expand Down
7 changes: 7 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
use Mix.Config

config :ex_sieve,
custom_predicates: [
has_key: "? \\? ?",
not_liked: "(? ->> 'score') :: int < 6",
key_is: "(? ->> ?) = ?"
]

config :ex_sieve, ecto_repos: [ExSieve.Repo]

config :ex_sieve, ExSieve.Repo,
Expand Down
3 changes: 2 additions & 1 deletion coveralls.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"skip_files": [
"test"
"test",
"lib/ex_sieve/custom_predicate.ex"
],
"coverage_options": {
"treat_no_relevant_lines_as_covered": true
Expand Down

0 comments on commit cdf2b4a

Please sign in to comment.