ash_postgres/test/load_test.exs
2024-07-08 15:56:58 -04:00

987 lines
29 KiB
Elixir

defmodule AshPostgres.Test.LoadTest do
use AshPostgres.RepoCase, async: false
alias AshPostgres.Test.{Author, Comment, Post, Record, StatefulPostFollower, TempEntity, User}
require Ash.Query
test "has_many relationships can be loaded" do
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
post =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
Comment
|> Ash.Changeset.for_create(:create, %{title: "match"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Ash.create!()
results =
Post
|> Ash.Query.load(:comments)
|> Ash.read!()
assert [%Post{comments: [%{title: "match"}]}] = results
end
test "has_one relationships properly limit in the data layer" do
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
post =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
Comment
|> Ash.Changeset.for_create(:create, %{title: "match"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Ash.create!()
:timer.sleep(1)
assert %{id: second_comment_id} =
Comment
|> Ash.Changeset.for_create(:create, %{title: "match"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Ash.create!()
assert %{id: ^second_comment_id} =
Post
|> Ash.Query.load(latest_comment: [:expects_only_one_comment])
|> Ash.read_one!()
|> Map.get(:latest_comment)
end
test "belongs_to relationships can be loaded" do
assert %Comment{post: %Ash.NotLoaded{type: :relationship}} =
comment =
Comment
|> Ash.Changeset.for_create(:create, %{})
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "match"})
|> Ash.Changeset.manage_relationship(:comments, [comment], type: :append_and_remove)
|> Ash.create!()
results =
Comment
|> Ash.Query.load(:post)
|> Ash.read!()
assert [%Comment{post: %{title: "match"}}] = results
end
test "many_to_many loads work" do
source_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "source"})
|> Ash.create!()
destination_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|> Ash.create!()
destination_post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|> Ash.create!()
source_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
type: :append_and_remove
)
|> Ash.update!()
results =
source_post
|> Ash.load!(:linked_posts)
assert %{linked_posts: [%{title: "destination"}, %{title: "destination"}]} = results
end
test "many_to_many loads work with filter on join relationship" do
followers =
for i <- 0..2 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:stateful_followers, followers, type: :append_and_remove)
|> Ash.create!()
StatefulPostFollower
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.limit(1)
|> Ash.read_one!()
|> Ash.Changeset.for_update(:update, %{state: :inactive})
|> Ash.update!()
[post] =
Post
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.load(:active_followers)
|> Ash.read!()
assert length(post.active_followers) == 2
end
test "many_to_many loads work when nested" do
source_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "source"})
|> Ash.create!()
destination_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|> Ash.create!()
source_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post],
type: :append_and_remove
)
|> Ash.update!()
destination_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [source_post], type: :append_and_remove)
|> Ash.update!()
results =
source_post
|> Ash.load!(linked_posts: :linked_posts)
assert %{linked_posts: [%{title: "destination", linked_posts: [%{title: "source"}]}]} =
results
end
describe "lateral join loads" do
test "parent references are resolved" do
post1 =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
post2_id = post2.id
post3 =
Post
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|> Ash.create!()
assert [%{posts_with_matching_title: [%{id: ^post2_id}]}] =
Post
|> Ash.Query.load(:posts_with_matching_title)
|> Ash.Query.filter(id == ^post1.id)
|> Ash.read!()
assert [%{posts_with_matching_title: []}] =
Post
|> Ash.Query.load(:posts_with_matching_title)
|> Ash.Query.filter(id == ^post3.id)
|> Ash.read!()
end
test "parent references work when joining for filters" do
%{id: post1_id} =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|> Ash.create!()
assert [%{id: ^post1_id}] =
Post
|> Ash.Query.filter(posts_with_matching_title.id == ^post2.id)
|> Ash.read!()
end
test "lateral join loads (loads with limits or offsets) are supported" do
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
post =
Post
|> Ash.Changeset.for_create(:create, %{title: "title"})
|> Ash.create!()
Comment
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Ash.create!()
Comment
|> Ash.Changeset.for_create(:create, %{title: "def"})
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|> Ash.create!()
comments_query =
Comment
|> Ash.Query.limit(1)
|> Ash.Query.sort(:title)
results =
Post
|> Ash.Query.load(comments: comments_query)
|> Ash.read!()
assert [%Post{comments: [%{title: "abc"}]}] = results
comments_query =
Comment
|> Ash.Query.limit(1)
|> Ash.Query.sort(title: :desc)
results =
Post
|> Ash.Query.load(comments: comments_query)
|> Ash.read!()
assert [%Post{comments: [%{title: "def"}]}] = results
comments_query =
Comment
|> Ash.Query.limit(2)
|> Ash.Query.sort(title: :desc)
results =
Post
|> Ash.Query.load(comments: comments_query)
|> Ash.read!()
assert [%Post{comments: [%{title: "def"}, %{title: "abc"}]}] = results
end
test "loading many to many relationships on records works without loading its join relationship when using code interface" do
source_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "source"})
|> Ash.create!()
destination_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|> Ash.create!()
destination_post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "def"})
|> Ash.create!()
source_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
type: :append_and_remove
)
|> Ash.update!()
assert %{linked_posts: [_, _]} = Post.get_by_id!(source_post.id, load: [:linked_posts])
end
test "lateral join loads with many to many relationships are supported" do
source_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "source"})
|> Ash.create!()
destination_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|> Ash.create!()
destination_post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "def"})
|> Ash.create!()
source_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
type: :append_and_remove
)
|> Ash.update!()
linked_posts_query =
Post
|> Ash.Query.limit(1)
|> Ash.Query.sort(title: :asc)
results =
source_post
|> Ash.load!(linked_posts: linked_posts_query)
assert %{linked_posts: [%{title: "abc"}]} = results
linked_posts_query =
Post
|> Ash.Query.limit(2)
|> Ash.Query.sort(title: :asc)
results =
source_post
|> Ash.load!(linked_posts: linked_posts_query)
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
end
test "lateral join loads with many to many relationships are supported with aggregates" do
source_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "source"})
|> Ash.create!()
destination_post =
Post
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|> Ash.create!()
destination_post2 =
Post
|> Ash.Changeset.for_create(:create, %{title: "def"})
|> Ash.create!()
source_post
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
type: :append_and_remove
)
|> Ash.update!()
linked_posts_query =
Post
|> Ash.Query.limit(1)
|> Ash.Query.sort(title: :asc)
results =
source_post
|> Ash.load!(linked_posts: linked_posts_query)
assert %{linked_posts: [%{title: "abc"}]} = results
linked_posts_query =
Post
|> Ash.Query.limit(2)
|> Ash.Query.sort(title: :asc)
|> Ash.Query.filter(count_of_comments_called_match == 0)
results =
source_post
|> Ash.load!(linked_posts: linked_posts_query)
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
end
test "lateral join loads with read action from a custom table and schema" do
record = Record |> Ash.Changeset.for_create(:create, %{full_name: "name"}) |> Ash.create!()
temp_entity =
TempEntity |> Ash.Changeset.for_create(:create, %{full_name: "name"}) |> Ash.create!()
assert %{entity: entity} = Ash.load!(record, :entity)
assert temp_entity.id == entity.id
end
end
describe "relationship pagination" do
test "it allows paginating has_many relationships with offset pagination" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
for i <- 0..9 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|> Ash.create!()
end
paginated_posts =
Post
|> Ash.Query.for_read(:paginated)
|> Ash.Query.page(limit: 2, offset: 2)
|> Ash.Query.sort(:title)
assert [author1, author2] =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!()
assert %Ash.Page.Offset{
results: [%{title: "author1 post2"}, %{title: "author1 post3"}]
} = author1.posts
assert %Ash.Page.Offset{
results: [%{title: "author2 post2"}, %{title: "author2 post3"}]
} = author2.posts
assert %Ash.Page.Offset{
results: [%{title: "author1 post4"}, %{title: "author1 post5"}]
} = Ash.page!(author1.posts, :next)
end
test "it allows paginating has_many relationships with keyset pagination" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
for i <- 0..9 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|> Ash.create!()
end
paginated_posts =
Post
|> Ash.Query.for_read(:keyset)
|> Ash.Query.page(limit: 2)
|> Ash.Query.sort(:title)
assert [author1, author2] =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!()
assert %Ash.Page.Keyset{
results: [%{title: "author1 post0"}, %{title: "author1 post1"}]
} = author1.posts
assert %Ash.Page.Keyset{
results: [%{title: "author2 post0"}, %{title: "author2 post1"}]
} = author2.posts
assert %Ash.Page.Keyset{
results: [%{title: "author1 post2"}, %{title: "author1 post3"}]
} = Ash.page!(author1.posts, :next)
end
test "it allows paginating many_to_many relationships with offset pagination" do
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_0_to_6 = Enum.take(followers, 6)
followers_5_to_9 = Enum.slice(followers, 5..9)
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6, type: :append_and_remove)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "b"})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9, type: :append_and_remove)
|> Ash.create!()
paginated_followers =
User
|> Ash.Query.page(limit: 2)
|> Ash.Query.sort(:name)
assert [post1, post2] =
Post
|> Ash.Query.sort(:title)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.read!()
assert %Ash.Page.Offset{
results: [%{name: "user0"}, %{name: "user1"}]
} = post1.followers
assert %Ash.Page.Offset{
results: [%{name: "user5"}, %{name: "user6"}]
} = post2.followers
assert %Ash.Page.Offset{
results: [%{name: "user2"}, %{name: "user3"}]
} = Ash.page!(post1.followers, :next)
end
test "it allows paginating many_to_many relationships with keyset pagination" do
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}"})
|> Ash.create!()
end
followers_0_to_6 = Enum.take(followers, 6)
followers_5_to_9 = Enum.slice(followers, 5..9)
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6, type: :append_and_remove)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "b"})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9, type: :append_and_remove)
|> Ash.create!()
paginated_followers =
User
|> Ash.Query.for_read(:keyset)
|> Ash.Query.page(limit: 2)
|> Ash.Query.sort(:name)
assert [post1, post2] =
Post
|> Ash.Query.sort(:title)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.read!()
assert %Ash.Page.Keyset{
results: [%{name: "user0"}, %{name: "user1"}]
} = post1.followers
assert %Ash.Page.Keyset{
results: [%{name: "user5"}, %{name: "user6"}]
} = post2.followers
assert %Ash.Page.Keyset{
results: [%{name: "user2"}, %{name: "user3"}]
} = Ash.page!(post1.followers, :next)
end
test "it allows paginating calculation ordered many_to_many relationships with offset" do
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_0_to_6_reversed =
Enum.take(followers, 7)
|> Enum.with_index()
|> Enum.map(fn {follower, idx} -> %{id: follower.id, order: 6 - idx} end)
followers_5_to_9_reversed =
Enum.slice(followers, 5..9)
|> Enum.with_index()
|> Enum.map(fn {follower, idx} -> %{id: follower.id, order: 9 - idx} end)
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6_reversed,
on_lookup: {:relate_and_update, :create, :read, [:order]}
)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "b"})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9_reversed,
on_lookup: {:relate_and_update, :create, :read, [:order]}
)
|> Ash.create!()
paginated_sorted_followers =
User
|> Ash.Query.page(limit: 2)
assert [post1, post2] =
Post
|> Ash.Query.sort(:title)
|> Ash.Query.load(sorted_followers: paginated_sorted_followers)
|> Ash.read!()
assert %Ash.Page.Offset{
results: [%{name: "user6"}, %{name: "user5"}]
} = post1.sorted_followers
assert %Ash.Page.Offset{
results: [%{name: "user9"}, %{name: "user8"}]
} = post2.sorted_followers
assert %Ash.Page.Offset{
results: [%{name: "user4"}, %{name: "user3"}]
} = Ash.page!(post1.sorted_followers, :next)
end
test "it allows paginating calculation ordered many_to_many relationships with keyset" do
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_0_to_6_reversed =
Enum.take(followers, 7)
|> Enum.with_index()
|> Enum.map(fn {follower, idx} -> %{id: follower.id, order: 6 - idx} end)
followers_5_to_9_reversed =
Enum.slice(followers, 5..9)
|> Enum.with_index()
|> Enum.map(fn {follower, idx} -> %{id: follower.id, order: 9 - idx} end)
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6_reversed,
on_lookup: {:relate_and_update, :create, :read, [:order]}
)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "b"})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9_reversed,
on_lookup: {:relate_and_update, :create, :read, [:order]}
)
|> Ash.create!()
paginated_sorted_followers =
User
|> Ash.Query.for_read(:keyset)
|> Ash.Query.page(limit: 2)
assert [post1, post2] =
Post
|> Ash.Query.sort(:title)
|> Ash.Query.load(sorted_followers: paginated_sorted_followers)
|> Ash.read!()
assert %Ash.Page.Keyset{
results: [%{name: "user6"}, %{name: "user5"}]
} = post1.sorted_followers
assert %Ash.Page.Keyset{
results: [%{name: "user9"}, %{name: "user8"}]
} = post2.sorted_followers
assert %Ash.Page.Keyset{
results: [%{name: "user4"}, %{name: "user3"}]
} = Ash.page!(post1.sorted_followers, :next)
end
test "works when nested with offset" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_0_to_6 = Enum.take(followers, 6)
followers_5_to_9 = Enum.slice(followers, 5..9)
for i <- 0..5 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6,
type: :append_and_remove
)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9,
type: :append_and_remove
)
|> Ash.create!()
end
paginated_followers =
User
|> Ash.Query.page(limit: 1)
|> Ash.Query.sort(:name)
paginated_posts =
Post
|> Ash.Query.for_read(:paginated)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.Query.page(limit: 1)
|> Ash.Query.sort(:title)
assert %Ash.Page.Offset{results: [author1]} =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!(page: [limit: 1])
assert %Ash.Page.Offset{
results: [
%{
title: "author1 post0",
followers: %Ash.Page.Offset{results: [%{name: "user0"}]} = followers_page
}
]
} = author1.posts
assert %Ash.Page.Offset{results: [%{title: "author1 post1"}]} =
Ash.page!(author1.posts, :next)
assert %Ash.Page.Offset{results: [%{name: "user1"}]} = Ash.page!(followers_page, :next)
end
test "works when nested with keyset" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
followers =
for i <- 0..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_0_to_6 = Enum.take(followers, 6)
followers_5_to_9 = Enum.slice(followers, 5..9)
for i <- 0..5 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.Changeset.manage_relationship(:followers, followers_0_to_6,
type: :append_and_remove
)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|> Ash.Changeset.manage_relationship(:followers, followers_5_to_9,
type: :append_and_remove
)
|> Ash.create!()
end
paginated_followers =
User
|> Ash.Query.for_read(:keyset)
|> Ash.Query.page(limit: 1)
|> Ash.Query.sort(:name)
paginated_posts =
Post
|> Ash.Query.for_read(:keyset)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.Query.page(limit: 1)
|> Ash.Query.sort(:title)
assert [author1, _author2] =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!()
assert %Ash.Page.Keyset{
results: [
%{
title: "author1 post0",
followers: %Ash.Page.Keyset{results: [%{name: "user0"}]} = followers_page
}
]
} = author1.posts
assert %Ash.Page.Keyset{results: [%{title: "author1 post1"}]} =
Ash.page!(author1.posts, :next)
assert %Ash.Page.Keyset{results: [%{name: "user1"}]} = Ash.page!(followers_page, :next)
end
test "it allows counting has_many relationships" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
for i <- 1..3 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.create!()
end
for i <- 1..6 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|> Ash.create!()
end
paginated_posts =
Post
|> Ash.Query.for_read(:paginated)
|> Ash.Query.page(limit: 2, offset: 2, count: true)
assert [author1, author2] =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!()
assert %Ash.Page.Offset{count: 3} = author1.posts
assert %Ash.Page.Offset{count: 6} = author2.posts
end
test "it allows counting many_to_many relationships" do
followers =
for i <- 1..9 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
followers_1_to_3 = Enum.take(followers, 3)
followers_4_to_9 = Enum.slice(followers, 3..9)
Post
|> Ash.Changeset.for_create(:create, %{title: "a"})
|> Ash.Changeset.manage_relationship(:followers, followers_1_to_3, type: :append_and_remove)
|> Ash.create!()
Post
|> Ash.Changeset.for_create(:create, %{title: "b"})
|> Ash.Changeset.manage_relationship(:followers, followers_4_to_9, type: :append_and_remove)
|> Ash.create!()
paginated_followers =
User
|> Ash.Query.page(limit: 2, count: true)
|> Ash.Query.sort(:name)
assert [post1, post2] =
Post
|> Ash.Query.sort(:title)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.read!()
assert %Ash.Page.Offset{count: 3} = post1.followers
assert %Ash.Page.Offset{count: 6} = post2.followers
end
test "allows counting nested relationships" do
author1 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
_author2 =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "b"})
|> Ash.create!()
followers =
for i <- 1..3 do
User
|> Ash.Changeset.for_create(:create, %{name: "user#{i}", is_active: true})
|> Ash.create!()
end
for i <- 1..5 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|> Ash.Changeset.manage_relationship(:followers, followers, type: :append_and_remove)
|> Ash.create!()
end
paginated_followers =
User
|> Ash.Query.page(limit: 1, count: true)
paginated_posts =
Post
|> Ash.Query.for_read(:paginated)
|> Ash.Query.load(followers: paginated_followers)
|> Ash.Query.page(limit: 1, count: true)
assert %Ash.Page.Offset{results: [author1], count: 2} =
Author
|> Ash.Query.sort(:first_name)
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!(page: [limit: 1, count: true])
assert %Ash.Page.Offset{count: 5, results: [%{followers: %Ash.Page.Offset{count: 3}}]} =
author1.posts
end
test "doesn't leak the internal count aggregate when counting" do
author =
Author
|> Ash.Changeset.for_create(:create, %{first_name: "a"})
|> Ash.create!()
for i <- 1..3 do
Post
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author.id})
|> Ash.create!()
end
paginated_posts =
Post
|> Ash.Query.for_read(:paginated)
|> Ash.Query.page(limit: 2, offset: 2, count: true)
assert [author] =
Author
|> Ash.Query.load(posts: paginated_posts)
|> Ash.read!()
assert %Ash.Page.Offset{count: 3} = author.posts
assert %{} == author.aggregates
end
end
end