Skip to content

Commit 2628bf2

Browse files
committed
WIP: enable hooking into build_assoc functions
1 parent 32ec822 commit 2628bf2

File tree

2 files changed

+92
-19
lines changed

2 files changed

+92
-19
lines changed

lib/predicate_converter.ex

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -451,28 +451,38 @@ defmodule Predicates.PredicateConverter do
451451
defp convert_any({:assoc, field}, sub_predicate, queryable, meta) do
452452
schema = get_schema(queryable)
453453

454-
sub_association = get_association_field(schema, field)
455-
456-
if is_nil(sub_association) do
457-
raise PredicateError,
458-
message: "Field '#{field}' in schema #{inspect(schema)} is not an association"
459-
else
460-
parent_table_name = get_table_name(sub_association.owner)
461-
462-
sub_schema = get_schema(sub_association)
454+
subquery =
455+
try do
456+
subquery = safe_call({schema, :build_assoc}, [field, meta], 1)
463457

464-
# define the subquery to execute the given predicates against the defined association
465-
subquery =
466-
from(s in sub_schema,
467-
select: 1,
468-
where:
469-
field(s, ^sub_association.related_key) ==
470-
field(parent_as(^parent_table_name), ^sub_association.owner_key)
458+
subquery
459+
|> where(
460+
^convert_query(subquery, sub_predicate, Map.put(meta, :__nested_virtual_json__, true))
471461
)
472-
|> build_sub_query(sub_predicate, meta)
462+
rescue
463+
[FunctionClauseError, UndefinedFunctionError] ->
464+
sub_association = get_association_field(schema, field)
465+
466+
if is_nil(sub_association) do
467+
raise PredicateError,
468+
message: "Field '#{field}' in schema #{inspect(schema)} is not an association"
469+
else
470+
parent_table_name = get_table_name(sub_association.owner)
471+
472+
sub_schema = get_schema(sub_association)
473+
474+
# define the subquery to execute the given predicates against the defined association
475+
from(s in sub_schema,
476+
select: 1,
477+
where:
478+
field(s, ^sub_association.related_key) ==
479+
field(parent_as(^parent_table_name), ^sub_association.owner_key)
480+
)
481+
|> build_sub_query(sub_predicate, meta)
482+
end
483+
end
473484

474-
dynamic(exists(subquery))
475-
end
485+
dynamic(exists(subquery))
476486
end
477487

478488
defp convert_any({:single, field}, sub_predicate, queryable, meta) do

test/predicates_test.exs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@ defmodule PredicatesTest do
66
alias Predicates.PredicateConverter, as: Converter
77
alias Predicates.PredicateError
88

9+
defmodule Prizes do
10+
@moduledoc false
11+
use Ecto.Schema
12+
13+
schema "pred_prizes" do
14+
field :title, :string
15+
field :year, :integer
16+
17+
belongs_to :author, PredicatesTest.Author
18+
end
19+
20+
def migrate do
21+
"""
22+
CREATE TABLE #{:pred_prizes} (
23+
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
24+
title text,
25+
year int,
26+
author_id int REFERENCES pred_authors(id) ON DELETE CASCADE ON UPDATE CASCADE
27+
)
28+
"""
29+
end
30+
end
31+
932
defmodule Post do
1033
@moduledoc false
1134
use Ecto.Schema
@@ -62,6 +85,7 @@ defmodule PredicatesTest do
6285
field :total_tags, {:array, :map}, virtual: true
6386

6487
has_many :posts, Post
88+
has_many :prizes, Prizes
6589
end
6690

6791
def migrate do
@@ -149,14 +173,27 @@ defmodule PredicatesTest do
149173
}
150174
)
151175
)
176+
177+
def build_assoc(:prizes, meta),
178+
do:
179+
subquery(
180+
from(p in Prizes,
181+
where: p.author_id == parent_as(:pred_authors).id,
182+
select: %{
183+
__element__: fragment("jsonb_build_object('name', ?, 'year', ?)", p.title, p.year)
184+
}
185+
)
186+
)
152187
end
153188

154189
alias __MODULE__.Author
155190
alias __MODULE__.Post
191+
alias __MODULE__.Prizes
156192

157193
def create_tables(_) do
158194
Predicates.Repo.query!(Author.migrate(), [])
159195
Predicates.Repo.query!(Post.migrate(), [])
196+
Predicates.Repo.query!(Prizes.migrate(), [])
160197

161198
:ok
162199
end
@@ -886,6 +923,32 @@ defmodule PredicatesTest do
886923
)
887924
|> Predicates.Repo.all()
888925
end
926+
927+
test "association with a build_assoc hook" do
928+
{2, [hauptmann, _]} =
929+
Predicates.Repo.insert_all(Author, [%{name: "Hauptmann"}, %{name: "Schiller"}],
930+
returning: true
931+
)
932+
933+
Predicates.Repo.insert_all(Prizes, [
934+
%{title: "Nobel Prize", year: 1912, author_id: hauptmann.id}
935+
])
936+
937+
assert [hauptmann] =
938+
Converter.build_query(
939+
Author,
940+
%{
941+
"op" => "any",
942+
"path" => "prizes",
943+
"arg" => %{
944+
"op" => "eq",
945+
"path" => "name",
946+
"arg" => "Nobel Prize"
947+
}
948+
}
949+
)
950+
|> Predicates.Repo.all()
951+
end
889952
end
890953

891954
describe "conjunctions" do

0 commit comments

Comments
 (0)