Skip to content

Commit

Permalink
Added support for delete_all
Browse files Browse the repository at this point in the history
  • Loading branch information
evadne committed Jun 28, 2022
1 parent 2af8316 commit 31f2c3c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 18 deletions.
47 changes: 41 additions & 6 deletions lib/etso/adapter/behaviour/queryable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,56 @@ defmodule Etso.Adapter.Behaviour.Queryable do
alias Etso.ETS.ObjectsSorter

@impl Ecto.Adapter.Queryable
def prepare(:all, query) do
{:nocache, query}
def prepare(:all, %Ecto.Query{} = query) do
{:nocache, {:select, query}}
end

@impl Ecto.Adapter.Queryable
def execute(%{repo: repo}, _, {:nocache, query}, params, _) do
def prepare(:delete_all, %Ecto.Query{wheres: []} = query) do
{:nocache, {:delete_all_objects, query}}
end

@impl Ecto.Adapter.Queryable
def prepare(:delete_all, %Ecto.Query{wheres: _} = query) do
{:nocache, {:match_delete, query}}
end

@impl Ecto.Adapter.Queryable
def execute(%{repo: repo}, _, {:nocache, {:select, query}}, params, _) do
{_, schema} = query.from.source
{:ok, ets_table} = TableRegistry.get_table(repo, schema)
ets_match = MatchSpecification.build(query, params)
ets_objects = :ets.select(ets_table, [ets_match]) |> ObjectsSorter.sort(query)
{length(ets_objects), ets_objects}
ets_objects = :ets.select(ets_table, [ets_match])
ets_count = length(ets_objects)
{ets_count, ObjectsSorter.sort(ets_objects, query)}
end

@impl Ecto.Adapter.Queryable
def execute(%{repo: repo}, _, {:nocache, {:delete_all_objects, query}}, params, _) do
{_, schema} = query.from.source
{:ok, ets_table} = TableRegistry.get_table(repo, schema)
ets_match = MatchSpecification.build(query, params)
ets_objects = query.select && ObjectsSorter.sort(:ets.select(ets_table, [ets_match]), query)
ets_count = :ets.info(ets_table, :size)
true = :ets.delete_all_objects(ets_table)
{ets_count, ets_objects || nil}
end

@impl Ecto.Adapter.Queryable
def execute(%{repo: repo}, _, {:nocache, {:match_delete, query}}, params, _) do
{_, schema} = query.from.source
{:ok, ets_table} = TableRegistry.get_table(repo, schema)
ets_match = MatchSpecification.build(query, params)
ets_objects = query.select && ObjectsSorter.sort(:ets.select(ets_table, [ets_match]), query)

{ets_match_head, ets_match_body, _} = ets_match
ets_match = {ets_match_head, ets_match_body, [true]}
ets_count = :ets.select_delete(ets_table, [ets_match])
{ets_count, ets_objects || nil}
end

@impl Ecto.Adapter.Queryable
def stream(%{repo: repo}, _, {:nocache, query}, params, options) do
def stream(%{repo: repo}, _, {:nocache, {:select, query}}, params, options) do
{_, schema} = query.from.source
{:ok, ets_table} = TableRegistry.get_table(repo, schema)
ets_match = MatchSpecification.build(query, params)
Expand Down
14 changes: 9 additions & 5 deletions lib/etso/ets/match_specification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ defmodule Etso.ETS.MatchSpecification do
{_, schema} = query.from.source
field_names = Etso.ETS.TableStructure.field_names(schema)
match_head = build_head(field_names)
match_conditions = build_conditions(field_names, params, query.wheres)
match_body = [build_body(field_names, query.select.fields)]
match_conditions = build_conditions(field_names, params, query)
match_body = [build_body(field_names, query)]
{match_head, match_conditions, match_body}
end

defp build_head(field_names) do
List.to_tuple(Enum.map(1..length(field_names), fn x -> :"$#{x}" end))
end

defp build_conditions(field_names, params, query_wheres) do
Enum.reduce(query_wheres, [], fn %Ecto.Query.BooleanExpr{expr: expression}, acc ->
defp build_conditions(field_names, params, %Ecto.Query{wheres: wheres}) do
Enum.reduce(wheres, [], fn %Ecto.Query.BooleanExpr{expr: expression}, acc ->
[build_condition(field_names, params, expression) | acc]
end)
end
Expand Down Expand Up @@ -76,7 +76,11 @@ defmodule Etso.ETS.MatchSpecification do
value
end

defp build_body(field_names, fields) do
defp build_body(field_names, %Ecto.Query{select: nil} = query) do
[]
end

defp build_body(field_names, %Ecto.Query{select: %{fields: fields}}) do
for field <- fields do
resolve_field_target(field_names, field)
end
Expand Down
50 changes: 43 additions & 7 deletions test/northwind/repo_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Northwind.RepoTest do
:ok = Importer.perform()
end

test "list" do
test "List All" do
Repo.all(Model.Employee)
end

Expand Down Expand Up @@ -175,20 +175,44 @@ defmodule Northwind.RepoTest do
assert sorted_etso == sorted_code
end

describe "json_extract_path" do
test "Support json_extract_path expression" do
test "Delete All" do
assert Repo.delete_all(Model.Employee)
assert [] == Repo.all(Model.Employee)
end

test "Delete Where" do
query = Model.Employee |> where([e], e.employee_id in [1, 5])
assert [a, b] = Repo.all(query)
assert {2, nil} = Repo.delete_all(query)
assert [] == Repo.all(query)
refute [] == Repo.all(Model.Employee)
end

test "Delete Where Select" do
query = Model.Employee |> where([e], e.employee_id in [1, 5])
assert [a, b] = Repo.all(query)
assert {2, list} = Repo.delete_all(query |> select([e], {e, e.employee_id}))
assert is_list(list)
assert Enum.any?(list, &(elem(&1, 1) == 1))
assert Enum.any?(list, &(elem(&1, 1) == 5))
assert [] = Repo.all(query)
refute [] == Repo.all(Model.Employee)
end

describe "With JSON Extract Paths" do
test "using literal value" do
Model.Employee
|> where([e], e.metadata["twitter"] == "@andrew_fuller")
|> Repo.one!()
end

test "Support nested json_extract_path expression" do
test "using brackets" do
Model.Employee
|> where([e], e.metadata["documents"]["passport"] == "verified")
|> Repo.one!()
end

test "Support variable pinning in nested json_extract_path expression" do
test "with variable pinning" do
field = "passport"

Model.Employee
Expand All @@ -202,7 +226,7 @@ defmodule Northwind.RepoTest do
|> assert()
end

test "Support accessing JSON arrays in json_extract_path expression" do
test "with arrays" do
Model.Employee
|> select([e], json_extract_path(e.metadata, ["photos", 0, "url"]))
|> where([e], e.metadata["documents"]["passport"] == "verified")
Expand All @@ -225,13 +249,25 @@ defmodule Northwind.RepoTest do
|> assert()
end

test "Support where/in" do
test "with where/in" do
Model.Employee
|> where([e], e.metadata["documents"]["passport"] in ~w(verified))
|> select([e], e.metadata["photos"][1]["url"])
|> Repo.one!()
|> (&(&1 == "https://example.com/b")).()
|> assert()
end

test "in deletion" do
Model.Employee
|> where([e], e.metadata["documents"]["passport"] == "verified")
|> Repo.delete_all()

assert_raise Ecto.NoResultsError, fn ->
Model.Employee
|> where([e], e.metadata["documents"]["passport"] == "verified")
|> Repo.one!()
end
end
end
end

0 comments on commit 31f2c3c

Please sign in to comment.