@@ -8,6 +8,7 @@ defmodule AshSql.AggregateTest do
88 alias AshPostgres.Test . { Author , Chat , Comment , Organization , Post , Rating , User }
99
1010 require Ash.Query
11+ require Ash.Sort
1112 import Ash.Expr
1213
1314 test "nested sum aggregates" do
@@ -1975,6 +1976,81 @@ defmodule AshSql.AggregateTest do
19751976 end
19761977 end
19771978
1979+ test "aggregate with parent() ref in relationship filter and sorting on relationship field" do
1980+ chat_1 =
1981+ Chat
1982+ |> Ash.Changeset . for_create ( :create , % { name: "Test Chat" } )
1983+ |> Ash . create! ( )
1984+
1985+ chat_1_message_1 =
1986+ AshPostgres.Test.Message
1987+ |> Ash.Changeset . for_create ( :create , % {
1988+ chat_id: chat_1 . id ,
1989+ content: "First message" ,
1990+ sent_at: DateTime . add ( DateTime . utc_now ( ) , - 3600 , :second )
1991+ } )
1992+ |> Ash . create! ( )
1993+
1994+ _chat_1_message_2 =
1995+ AshPostgres.Test.Message
1996+ |> Ash.Changeset . for_create ( :create , % {
1997+ chat_id: chat_1 . id ,
1998+ content: "Second message" ,
1999+ sent_at: DateTime . add ( DateTime . utc_now ( ) , - 1800 , :second )
2000+ } )
2001+ |> Ash . create! ( )
2002+
2003+ # Update chat to set last_read_message to the first message
2004+ # This means message_2 should be "unread"
2005+ _chat =
2006+ chat_1
2007+ |> Ash.Changeset . for_update ( :update , % { last_read_message_id: chat_1_message_1 . id } )
2008+ |> Ash . update! ( )
2009+
2010+ # Create a second chat to force multiple records and trigger DISTINCT ON
2011+ chat_2 =
2012+ Chat
2013+ |> Ash.Changeset . for_create ( :create , % { name: "Test Chat 2" } )
2014+ |> Ash . create! ( )
2015+
2016+ chat_2_message_1 =
2017+ AshPostgres.Test.Message
2018+ |> Ash.Changeset . for_create ( :create , % {
2019+ chat_id: chat_2 . id ,
2020+ content: "Chat 2 - Message 1" ,
2021+ sent_at: DateTime . add ( DateTime . utc_now ( ) , - 100 , :second )
2022+ } )
2023+ |> Ash . create! ( )
2024+
2025+ AshPostgres.Test.Message
2026+ |> Ash.Changeset . for_create ( :create , % {
2027+ chat_id: chat_2 . id ,
2028+ content: "Chat 2 - Message 2" ,
2029+ sent_at: DateTime . utc_now ( )
2030+ } )
2031+ |> Ash . create! ( )
2032+
2033+ chat_2
2034+ |> Ash.Changeset . for_update ( :update , % { last_read_message_id: chat_2_message_1 . id } )
2035+ |> Ash . update! ( )
2036+
2037+ # This query exercises the following conditions:
2038+ # - select() excludes last_read_message_id from the query
2039+ # - Sorting by last_message.sent_at (has_one from_many?) causes DISTINCT ON + subquery wrapping
2040+ # - Loading unread_messages_count_alt (aggregate on relationship with parent() in filter)
2041+ # uses a lateral join that references parent(last_read_message_id)
2042+ # - The wrapped subquery must include last_read_message_id for the lateral join to work
2043+ result =
2044+ Chat
2045+ |> Ash.Query . filter ( id in [ ^ chat_1 . id , ^ chat_2 . id ] )
2046+ |> Ash.Query . select ( [ :id , :name ] )
2047+ |> Ash.Query . load ( :unread_messages_count_alt )
2048+ |> Ash.Query . sort ( [ { Ash.Sort . expr_sort ( expr ( last_message . sent_at ) ) , :asc } ] )
2049+ |> Ash . read! ( )
2050+
2051+ assert length ( result ) == 2
2052+ end
2053+
19782054 test "multiple aggregates filtering on nested first aggregate" do
19792055 post =
19802056 Post
0 commit comments