Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions test/aggregate_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
alias AshPostgres.Test.{Author, Chat, Comment, Organization, Post, Rating, User}

require Ash.Query
require Ash.Sort
import Ash.Expr

test "nested sum aggregates" do
Expand Down Expand Up @@ -101,7 +102,7 @@
assert read_post.count_of_comments == 1
end

test "nested filters on aggregates works" do

Check failure on line 105 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (16) / mix test

test loading optimizable first aggregate with relationship filter does not cause binding errors (AshSql.AggregateTest)

Check failure on line 105 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (15) / mix test

test loading optimizable first aggregate with relationship filter does not cause binding errors (AshSql.AggregateTest)

Check failure on line 105 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (14) / mix test

test loading optimizable first aggregate with relationship filter does not cause binding errors (AshSql.AggregateTest)
org =
Organization
|> Ash.Changeset.for_create(:create, %{name: "match"})
Expand Down Expand Up @@ -1920,6 +1921,81 @@
end
end

test "aggregate with parent() ref in relationship filter and sorting on relationship field" do
chat_1 =
Chat
|> Ash.Changeset.for_create(:create, %{name: "Test Chat"})
|> Ash.create!()

chat_1_message_1 =
AshPostgres.Test.Message
|> Ash.Changeset.for_create(:create, %{
chat_id: chat_1.id,
content: "First message",
sent_at: DateTime.add(DateTime.utc_now(), -3600, :second)
})
|> Ash.create!()

_chat_1_message_2 =
AshPostgres.Test.Message
|> Ash.Changeset.for_create(:create, %{
chat_id: chat_1.id,
content: "Second message",
sent_at: DateTime.add(DateTime.utc_now(), -1800, :second)
})
|> Ash.create!()

# Update chat to set last_read_message to the first message
# This means message_2 should be "unread"
_chat =
chat_1
|> Ash.Changeset.for_update(:update, %{last_read_message_id: chat_1_message_1.id})
|> Ash.update!()

# Create a second chat to force multiple records and trigger DISTINCT ON
chat_2 =
Chat
|> Ash.Changeset.for_create(:create, %{name: "Test Chat 2"})
|> Ash.create!()

chat_2_message_1 =
AshPostgres.Test.Message
|> Ash.Changeset.for_create(:create, %{
chat_id: chat_2.id,
content: "Chat 2 - Message 1",
sent_at: DateTime.add(DateTime.utc_now(), -100, :second)
})
|> Ash.create!()

AshPostgres.Test.Message
|> Ash.Changeset.for_create(:create, %{
chat_id: chat_2.id,
content: "Chat 2 - Message 2",
sent_at: DateTime.utc_now()
})
|> Ash.create!()

chat_2
|> Ash.Changeset.for_update(:update, %{last_read_message_id: chat_2_message_1.id})

Check failure on line 1979 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (16) / mix test

test aggregate with parent() ref in relationship filter and sorting on relationship field (AshSql.AggregateTest)

Check failure on line 1979 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (15) / mix test

test aggregate with parent() ref in relationship filter and sorting on relationship field (AshSql.AggregateTest)

Check failure on line 1979 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (14) / mix test

test aggregate with parent() ref in relationship filter and sorting on relationship field (AshSql.AggregateTest)
|> Ash.update!()

# This query exercises the following conditions:
# - select() excludes last_read_message_id from the query
# - Sorting by last_message.sent_at (has_one from_many?) causes DISTINCT ON + subquery wrapping
# - Loading unread_messages_count_alt (aggregate on relationship with parent() in filter)
# uses a lateral join that references parent(last_read_message_id)
# - The wrapped subquery must include last_read_message_id for the lateral join to work
result =
Chat
|> Ash.Query.filter(id in [^chat_1.id, ^chat_2.id])
|> Ash.Query.select([:id, :name])
|> Ash.Query.load(:unread_messages_count_alt)
|> Ash.Query.sort([{Ash.Sort.expr_sort(expr(last_message.sent_at)), :asc}])
|> Ash.read!()

assert length(result) == 2
end

test "multiple aggregates filtering on nested first aggregate" do
post =
Post
Expand Down
10 changes: 10 additions & 0 deletions test/support/resources/chat.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,22 @@ defmodule AshPostgres.Test.Chat do
filter(expr(is_nil(read_at)))
sort(sent_at: :desc)
end

has_many :unread_messages, AshPostgres.Test.Message do
public?(true)
no_attributes?(true)
filter(expr(is_nil(parent(last_read_message_id)) or id > parent(last_read_message_id)))
end
end

aggregates do
count :unread_message_count, :messages do
public?(true)
filter(expr(is_nil(parent(last_read_message_id)) or id > parent(last_read_message_id)))
end

count :unread_messages_count_alt, :unread_messages do
public?(true)
end
end
end
Loading