improvement: support new uniq? option on count/list aggregates

This commit is contained in:
Zach Daniel 2023-02-09 16:09:44 -05:00
parent b76289f39f
commit 8e8a308cd2
5 changed files with 68 additions and 10 deletions

View file

@ -764,18 +764,32 @@ defmodule AshPostgres.Aggregate do
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
distinct =
if Map.get(aggregate, :uniq?) do
"DISTINCT "
else
""
end
{:ok, expr} =
AshPostgres.Functions.Fragment.casted_new(
["array_agg(? ORDER BY #{question_marks})", field] ++ sort_expr
["array_agg(#{distinct}? ORDER BY #{question_marks})", field] ++ sort_expr
)
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
else
if Map.get(aggregate, :uniq?) do
Ecto.Query.dynamic(
[row],
fragment("array_agg(DISTINCT ?)", ^field)
)
else
Ecto.Query.dynamic(
[row],
fragment("array_agg(?)", ^field)
)
end
end
filtered = filter_field(sorted, query, aggregate, relationship_path, is_single?)
@ -841,7 +855,11 @@ defmodule AshPostgres.Aggregate do
field =
case kind do
:count ->
Ecto.Query.dynamic([row], count(^field))
if Map.get(aggregate, :uniq?) do
Ecto.Query.dynamic([row], count(^field, :distinct))
else
Ecto.Query.dynamic([row], count(^field, :distinct))
end
:sum ->
Ecto.Query.dynamic([row], sum(^field))

View file

@ -161,7 +161,7 @@ defmodule AshPostgres.MixProject do
{:ecto, "~> 3.9"},
{:jason, "~> 1.0"},
{:postgrex, ">= 0.0.0"},
{:ash, ash_version("~> 2.5")},
{:ash, ash_version("~> 2.6 and >= 2.6.1")},
{:git_ops, "~> 2.5", only: [:dev, :test]},
{:nimble_options, "~> 0.5"},
{:ex_doc, "~> 0.22", only: [:dev, :test], runtime: false},

View file

@ -1,5 +1,5 @@
%{
"ash": {:hex, :ash, "2.5.16", "32dbccf70588fd8e41e8d7cc8a0567eac11d82ea33b1997020b845416d582bf8", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 0.3.8 and < 1.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "64608980a63ab3cf1a061cd914abd096e9c653b5be133d84210f5d76ee63860f"},
"ash": {:hex, :ash, "2.6.1", "a9fab10e4039a34a917454a0cdd0bf971d784031f13f98a23bfb709336ad7106", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 0.3.12 and < 1.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6b8eb5ffa30fb8e048efaac46947485d53fe4259ddb5077f309165f6476ca284"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
@ -35,7 +35,7 @@
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
"sourceror": {:hex, :sourceror, "0.12.0", "b0a43f9b91d69fead8ffa307e203fadaf30001d8c2643f597518dd9508d6b32d", [:mix], [], "hexpm", "67dcc48a4d4bb8c397ad0b86cf0d4667e1b50ffdf31cda585524640d1caded85"},
"spark": {:hex, :spark, "0.3.12", "6754698b5aa27358365fff88e3c5c3abc138e165fbc394cf482e39779ee92cd6", [:mix], [{:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "27f13b604ddaf1d2b3b6ce5b3a21ed170e2f1235e167ab6fb8c2c33c8165d545"},
"spark": {:hex, :spark, "0.4.1", "72b4bdc67fb297dce209501a25db5f9e2c80ae22095d9bcf6acfdc9a445786c2", [:mix], [{:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "83ef6910bd3b47aa33403753c3211ff58f34287ea3e86b62063dfe3e1a1dee99"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},

View file

@ -218,6 +218,40 @@ defmodule AshPostgres.AggregateTest do
|> Ash.Query.load(:comment_titles)
|> Api.read_one!()
end
test "with related data, it returns the uniq" do
post =
Post
|> Ash.Changeset.new(%{title: "title"})
|> Api.create!()
Comment
|> Ash.Changeset.new(%{title: "aaa"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Api.create!()
Comment
|> Ash.Changeset.new(%{title: "aaa"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Api.create!()
assert %{uniq_comment_titles: ["aaa"]} =
Post
|> Ash.Query.filter(id == ^post.id)
|> Ash.Query.load(:uniq_comment_titles)
|> Api.read_one!()
Comment
|> Ash.Changeset.new(%{title: "bbb"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Api.create!()
assert %{uniq_comment_titles: ["aaa", "bbb"]} =
Post
|> Ash.Query.filter(id == ^post.id)
|> Ash.Query.load(:uniq_comment_titles)
|> Api.read_one!()
end
end
describe "first" do

View file

@ -26,10 +26,11 @@ defmodule AshPostgres.Test.Post do
end
custom_indexes do
index [:uniq_custom_one, :uniq_custom_two],
index([:uniq_custom_one, :uniq_custom_two],
unique: true,
concurrently: true,
message: "dude what the heck"
)
end
end
@ -199,6 +200,11 @@ defmodule AshPostgres.Test.Post do
sort(title: :asc_nils_last)
end
list :uniq_comment_titles, :comments, :title do
uniq?(true)
sort(title: :asc_nils_last)
end
list :comment_titles_with_5_likes, :comments, :title do
sort(title: :asc_nils_last)
filter(expr(likes >= 5))