Skip to content

Commit

Permalink
Merge pull request #22 from woylie/fix/filter-queries
Browse files Browse the repository at this point in the history
fix invalid filter queries
  • Loading branch information
woylie authored May 27, 2020
2 parents b041b83 + 818cf07 commit a0ee2ea
Show file tree
Hide file tree
Showing 21 changed files with 416 additions and 120 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ jobs:
otp: [21.3.8.9, 22.1.3]
elixir: [1.8.2, 1.9.2, 1.10.3]

services:
postgres:
image: postgres:12-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

## Unreleased

## [0.4.0] - 2020-05-27

## Added

- Added `=~` filter operator.

## Fixed

- Query function wasn't generating valid where clauses for filters.

## [0.3.0] - 2020-05-22

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Add `flop` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:flop, "~> 0.2.0"}
{:flop, "~> 0.4.0"}
]
end
```
Expand Down
3 changes: 3 additions & 0 deletions config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

import_config "#{Mix.env()}.exs"
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

import_config "#{Mix.env()}.exs"
1 change: 1 addition & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use Mix.Config
13 changes: 13 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use Mix.Config

config :flop,
ecto_repos: [Flop.Repo]

config :flop, Flop.Repo,
username: "postgres",
password: "postgres",
database: "flop_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox

config :logger, level: :warn
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: "3.7"
services:
postgres:
image: postgres:12-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
30 changes: 15 additions & 15 deletions lib/flop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Flop do
Add the `t:Flop.t/0` to a `t:Ecto.Queryable.t/0` with `Flop.query/2`.
iex> params = %{"order_by" => ["name", "age"], "limit" => 5}
iex> {:ok, flop} = Flop.validate(params, for: Pet)
iex> {:ok, flop} = Flop.validate(params, for: Flop.Pet)
{:ok,
%Flop{
filters: [],
Expand All @@ -35,8 +35,8 @@ defmodule Flop do
page: nil,
page_size: nil
}}
iex> Pet |> Flop.query(flop)
#Ecto.Query<from p0 in Pet, order_by: [asc: p0.name, asc: p0.age], \
iex> Flop.Pet |> Flop.query(flop)
#Ecto.Query<from p0 in Flop.Pet, order_by: [asc: p0.name, asc: p0.age], \
limit: ^5>
"""
use Ecto.Schema
Expand Down Expand Up @@ -112,15 +112,15 @@ defmodule Flop do
## Examples
iex> flop = %Flop{limit: 10, offset: 19}
iex> Flop.query(Pet, flop)
#Ecto.Query<from p0 in Pet, limit: ^10, offset: ^19>
iex> Flop.query(Flop.Pet, flop)
#Ecto.Query<from p0 in Flop.Pet, limit: ^10, offset: ^19>
Or enhance an already defined query:
iex> require Ecto.Query
iex> flop = %Flop{limit: 10}
iex> Pet |> Ecto.Query.where(species: "dog") |> Flop.query(flop)
#Ecto.Query<from p0 in Pet, where: p0.species == \"dog\", limit: ^10>
iex> Flop.Pet |> Ecto.Query.where(species: "dog") |> Flop.query(flop)
#Ecto.Query<from p0 in Flop.Pet, where: p0.species == \"dog\", limit: ^10>
"""
@spec query(Queryable.t(), Flop.t()) :: Queryable.t()
def query(q, flop) do
Expand Down Expand Up @@ -228,27 +228,27 @@ defmodule Flop do
end

def filter(q, %Filter{field: field, op: :==, value: value}),
do: Query.where(q, ^field == ^value)
do: Query.where(q, ^[{field, value}])

def filter(q, %Filter{field: field, op: :!=, value: value}),
do: Query.where(q, ^field != ^value)
do: Query.where(q, [r], field(r, ^field) != ^value)

def filter(q, %Filter{field: field, op: :=~, value: value}) do
query_value = "%#{value}%"
Query.where(q, ilike(^field, ^query_value))
Query.where(q, [r], ilike(field(r, ^field), ^query_value))
end

def filter(q, %Filter{field: field, op: :>=, value: value}),
do: Query.where(q, ^field >= ^value)
do: Query.where(q, [r], field(r, ^field) >= ^value)

def filter(q, %Filter{field: field, op: :<=, value: value}),
do: Query.where(q, ^field <= ^value)
do: Query.where(q, [r], field(r, ^field) <= ^value)

def filter(q, %Filter{field: field, op: :>, value: value}),
do: Query.where(q, ^field > ^value)
do: Query.where(q, [r], field(r, ^field) > ^value)

def filter(q, %Filter{field: field, op: :<, value: value}),
do: Query.where(q, ^field < ^value)
do: Query.where(q, [r], field(r, ^field) < ^value)

## Validation

Expand Down Expand Up @@ -294,7 +294,7 @@ defmodule Flop do
that only allowed fields are used.
iex> params = %{"order_by" => ["social_security_number"]}
iex> {:error, changeset} = Flop.validate(params, for: Pet)
iex> {:error, changeset} = Flop.validate(params, for: Flop.Pet)
iex> changeset.valid?
false
iex> [order_by: {msg, [_, {_, enum}]}] = changeset.errors
Expand Down
12 changes: 6 additions & 6 deletions lib/flop/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defprotocol Flop.Schema do
After that, you can pass the module as the `:for` option to `Flop.validate/2`.
iex> Flop.validate(%Flop{order_by: [:name]}, for: Pet)
iex> Flop.validate(%Flop{order_by: [:name]}, for: Flop.Pet)
{:ok,
%Flop{
filters: [],
Expand All @@ -36,7 +36,7 @@ defprotocol Flop.Schema do
}}
iex> {:error, changeset} = Flop.validate(
...> %Flop{order_by: [:social_security_number]}, for: Pet
...> %Flop{order_by: [:social_security_number]}, for: Flop.Pet
...> )
iex> changeset.valid?
false
Expand Down Expand Up @@ -66,7 +66,7 @@ defprotocol Flop.Schema do
@doc """
Returns the filterable fields of a schema.
iex> Flop.Schema.filterable(%Pet{})
iex> Flop.Schema.filterable(%Flop.Pet{})
[:name, :species]
"""
@spec filterable(any) :: [atom]
Expand All @@ -75,7 +75,7 @@ defprotocol Flop.Schema do
@doc """
Returns the sortable fields of a schema.
iex> Flop.Schema.sortable(%Pet{})
iex> Flop.Schema.sortable(%Flop.Pet{})
[:name, :age, :species]
"""
@spec sortable(any) :: [atom]
Expand All @@ -84,7 +84,7 @@ defprotocol Flop.Schema do
@doc """
Returns the default limit of a schema.
iex> Flop.Schema.default_limit(%Fruit{})
iex> Flop.Schema.default_limit(%Flop.Fruit{})
50
"""
@spec default_limit(any) :: pos_integer | nil
Expand All @@ -93,7 +93,7 @@ defprotocol Flop.Schema do
@doc """
Returns the maximum limit of a schema.
iex> Flop.Schema.max_limit(%Pet{})
iex> Flop.Schema.max_limit(%Flop.Pet{})
20
"""
@spec max_limit(any) :: pos_integer | nil
Expand Down
12 changes: 10 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Flop.MixProject do
def project do
[
app: :flop,
version: "0.3.0",
version: "0.4.0",
elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
Expand All @@ -29,7 +29,8 @@ defmodule Flop.MixProject do
docs: [
main: "readme",
extras: ["README.md"]
]
],
aliases: aliases()
]
end

Expand All @@ -49,8 +50,11 @@ defmodule Flop.MixProject do
{:credo, "~> 1.4.0", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.0.0", only: [:dev], runtime: false},
{:ecto, "~> 3.2"},
{:ecto_sql, "~> 3.4", only: :test},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:ex_machina, "~> 2.4", only: :test},
{:excoveralls, "~> 0.10", only: :test},
{:postgrex, ">= 0.0.0", only: :test},
{:stream_data, "~> 0.5", only: [:dev, :test]}
]
end
Expand All @@ -66,4 +70,8 @@ defmodule Flop.MixProject do
files: ~w(lib .formatter.exs mix.exs README* LICENSE*)
]
end

defp aliases do
[test: ["ecto.create --quiet", "ecto.migrate", "test"]]
end
end
5 changes: 5 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"},
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"},
"earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"},
"ecto": {:hex, :ecto, "3.4.4", "a2c881e80dc756d648197ae0d936216c0308370332c5e77a2325a10293eef845", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4bd3ad62abc3b21fb629f0f7a3dab23a192fca837d257dd08449fba7373561"},
"ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.22.1", "9bb6d51508778193a4ea90fa16eac47f8b67934f33f8271d5e1edec2dc0eee4c", [:mix], [{:earmark, "~> 1.4.0", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "d957de1b75cb9f78d3ee17820733dc4460114d8b1e11f7ee4fd6546e69b1db60"},
"ex_machina": {:hex, :ex_machina, "2.4.0", "09a34c5d371bfb5f78399029194a8ff67aff340ebe8ba19040181af35315eabb", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "a20bc9ddc721b33ea913b93666c5d0bdca5cbad7a67540784ae277228832d72c"},
"excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
Expand All @@ -18,6 +22,7 @@
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"postgrex": {:hex, :postgrex, "0.15.4", "5d691c25fc79070705a2ff0e35ce0822b86a0ee3c6fdb7a4fb354623955e1aed", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "306515b9d975fcb2478dc337a1d27dc3bf8af7cd71017c333fe9db3a3d211b0a"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
Expand Down
5 changes: 5 additions & 0 deletions priv/repo/migrations/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
import_deps: [:ecto_sql],
inputs: ["*.exs"],
line_length: 80
]
16 changes: 16 additions & 0 deletions priv/repo/migrations/20200527145236_create_test_tables.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule Flop.Repo.Migrations.CreateTestTables do
use Ecto.Migration

def change do
create table(:pets) do
add(:name, :string)
add(:age, :integer)
add(:species, :string)
end

create table(:fruits) do
add(:name, :string)
add(:familiy, :string)
end
end
end
Loading

0 comments on commit a0ee2ea

Please sign in to comment.