2021-06-04 17:48:35 +12:00
|
|
|
defmodule AshPostgres.CalculationTest do
|
|
|
|
use AshPostgres.RepoCase, async: false
|
2023-11-22 03:33:13 +13:00
|
|
|
alias AshPostgres.Test.{Account, Api, Author, Comment, Post, User}
|
2021-06-04 17:48:35 +12:00
|
|
|
|
|
|
|
require Ash.Query
|
2023-09-21 08:41:32 +12:00
|
|
|
import Ash.Expr
|
2021-06-04 17:48:35 +12:00
|
|
|
|
|
|
|
test "an expression calculation can be filtered on" do
|
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post2 =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "title2"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "title3"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "_"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "_"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "_"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post
|
|
|
|
|> Ash.Changeset.new()
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.update!()
|
|
|
|
|
|
|
|
post2
|
|
|
|
|> Ash.Changeset.new()
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.update!()
|
|
|
|
|
|
|
|
assert [%{c_times_p: 6, title: "match"}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:c_times_p)
|
|
|
|
|> Api.read!()
|
|
|
|
|> Enum.filter(&(&1.c_times_p == 6))
|
|
|
|
|
|
|
|
assert [
|
|
|
|
%{c_times_p: %Ash.NotLoaded{}, title: "match"}
|
|
|
|
] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(c_times_p == 6)
|
|
|
|
|> Api.read!()
|
2023-11-23 07:37:54 +13:00
|
|
|
|
|
|
|
assert [] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(author: [has_posts: true])
|
|
|
|
|> Api.read!()
|
2021-06-04 17:48:35 +12:00
|
|
|
end
|
|
|
|
|
2023-10-10 11:57:06 +13:00
|
|
|
test "calculations can refer to to_one path attributes in filters" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Foo",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{author_first_name_calc: "Foo"}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(author_first_name_calc == "Foo")
|
|
|
|
|> Ash.Query.load(:author_first_name_calc)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "calculations can refer to to_one path attributes in sorts" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Foo",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{author_first_name_calc: "Foo"}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.sort(:author_first_name_calc)
|
|
|
|
|> Ash.Query.load(:author_first_name_calc)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
2022-02-08 10:48:36 +13:00
|
|
|
test "calculations can refer to embedded attributes" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{title: "Mr."} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:title)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
2022-07-21 06:19:06 +12:00
|
|
|
test "calculations can use the || operator" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{first_name_or_bob: "bob"} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:first_name_or_bob)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "calculations can use the && operator" do
|
|
|
|
author =
|
|
|
|
Author
|
2022-10-25 17:17:42 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "fred",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
2022-07-21 06:19:06 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{first_name_and_bob: "bob"} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:first_name_and_bob)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
2021-06-04 17:48:35 +12:00
|
|
|
test "calculations can be used in related filters" do
|
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post2 =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "title2"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "title3"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "no_match"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post2, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post
|
|
|
|
|> Ash.Changeset.new()
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.update!()
|
|
|
|
|
|
|
|
post2
|
|
|
|
|> Ash.Changeset.new()
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.update!()
|
|
|
|
|
|
|
|
posts_query =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:c_times_p)
|
|
|
|
|
|
|
|
assert %{post: %{c_times_p: 6}} =
|
|
|
|
Comment
|
|
|
|
|> Ash.Query.load(post: posts_query)
|
|
|
|
|> Api.read!()
|
|
|
|
|> Enum.filter(&(&1.post.c_times_p == 6))
|
|
|
|
|> Enum.at(0)
|
|
|
|
|
|
|
|
query =
|
|
|
|
Comment
|
|
|
|
|> Ash.Query.filter(post.c_times_p == 6)
|
|
|
|
|> Ash.Query.load(post: posts_query)
|
|
|
|
|> Ash.Query.limit(1)
|
|
|
|
|
|
|
|
assert [
|
|
|
|
%{post: %{c_times_p: 6, title: "match"}}
|
|
|
|
] = Api.read!(query)
|
2022-12-18 01:57:25 +13:00
|
|
|
|
|
|
|
post |> Api.load!(:c_times_p)
|
2021-06-04 17:48:35 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "concat calculation can be filtered on" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "not", last_name: "match"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
author_id = author.id
|
|
|
|
|
|
|
|
assert %{id: ^author_id} =
|
|
|
|
Author
|
2022-02-01 11:50:26 +13:00
|
|
|
|> Ash.Query.load(:full_name)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Ash.Query.filter(full_name == "is match")
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
2021-11-14 07:55:49 +13:00
|
|
|
test "calculations that refer to aggregates in comparison expressions can be filtered on" do
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:has_future_comment)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
2024-01-11 01:22:25 +13:00
|
|
|
test "calculations that refer to aggregates can be authorized" do
|
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "title"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "comment"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{has_future_comment: false} =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
|
|
|
|> Ash.Query.for_read(:allow_any, %{})
|
|
|
|
|> Api.read_one!(authorize?: true)
|
|
|
|
|
|
|
|
assert %{has_future_comment: true} =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
|
|
|
|> Ash.Query.for_read(:allow_any, %{})
|
|
|
|
|> Api.read_one!(authorize?: false)
|
|
|
|
|
|
|
|
assert %{has_future_comment: false} =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.for_read(:allow_any, %{})
|
|
|
|
|> Api.read_one!()
|
|
|
|
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: true)
|
|
|
|
|
|
|
|
assert %{has_future_comment: true} =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.for_read(:allow_any, %{})
|
|
|
|
|> Api.read_one!()
|
|
|
|
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: false)
|
|
|
|
end
|
|
|
|
|
2021-06-04 17:48:35 +12:00
|
|
|
test "conditional calculations can be filtered on" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
author_id = author.id
|
|
|
|
|
|
|
|
assert %{id: ^author_id} =
|
|
|
|
Author
|
2022-02-01 11:50:26 +13:00
|
|
|
|> Ash.Query.load([:conditional_full_name, :full_name])
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Ash.Query.filter(conditional_full_name == "(none)")
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "parameterized calculations can be filtered on" do
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{param_full_name: "tom holland"} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:param_full_name)
|
|
|
|
|> Api.read_one!()
|
|
|
|
|
|
|
|
assert %{param_full_name: "tom~holland"} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(param_full_name: [separator: "~"])
|
|
|
|
|> Api.read_one!()
|
|
|
|
|
|
|
|
assert %{} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(param_full_name(separator: "~") == "tom~holland")
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "parameterized related calculations can be filtered on" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "match"})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
2021-06-04 17:48:35 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{title: "match"} =
|
|
|
|
Comment
|
|
|
|
|> Ash.Query.filter(author.param_full_name(separator: "~") == "tom~holland")
|
|
|
|
|> Api.read_one!()
|
|
|
|
|
|
|
|
assert %{title: "match"} =
|
|
|
|
Comment
|
|
|
|
|> Ash.Query.filter(
|
|
|
|
author.param_full_name(separator: "~") == "tom~holland" and
|
|
|
|
author.param_full_name(separator: " ") == "tom holland"
|
|
|
|
)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
2021-06-06 10:13:20 +12:00
|
|
|
|
2021-09-14 04:58:23 +12:00
|
|
|
test "parameterized calculations can be sorted on" do
|
2021-06-06 10:13:20 +12:00
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "abc", last_name: "def"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{first_name: "abc"}, %{first_name: "tom"}] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(param_full_name: [separator: "~"])
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
2021-11-14 07:56:14 +13:00
|
|
|
|
2022-05-21 05:22:32 +12:00
|
|
|
test "calculations using if and literal boolean results can run" do
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:was_created_in_the_last_month)
|
2022-05-21 05:42:20 +12:00
|
|
|
|> Ash.Query.filter(was_created_in_the_last_month == true)
|
2022-05-21 05:22:32 +12:00
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
2022-02-01 08:36:46 +13:00
|
|
|
test "nested conditional calculations can be loaded" do
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{last_name: "holland"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.new(%{first_name: "tom"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{nested_conditional: "No First Name"}, %{nested_conditional: "No Last Name"}] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:nested_conditional)
|
|
|
|
|> Ash.Query.sort(:nested_conditional)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
2021-11-14 07:56:14 +13:00
|
|
|
test "calculations load nullable timestamp aggregates compared to a fragment" do
|
2021-11-14 08:48:14 +13:00
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|
|
|
|> Api.create!()
|
2021-11-14 07:56:14 +13:00
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "aaa", likes: 1, arbitrary_timestamp: DateTime.now!("Etc/UTC")})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-11-14 07:56:14 +13:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "bbb", likes: 1})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-11-14 07:56:14 +13:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "aaa", likes: 2})
|
2022-09-22 05:36:18 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
2021-11-14 07:56:14 +13:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load([:has_future_arbitrary_timestamp])
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
2022-12-18 01:57:25 +13:00
|
|
|
|
|
|
|
test "loading a calculation loads its dependent loads" do
|
|
|
|
user =
|
|
|
|
User
|
|
|
|
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
account =
|
|
|
|
Account
|
|
|
|
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
|
|
|
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|> Api.load!([:active])
|
|
|
|
|
|
|
|
assert account.active
|
|
|
|
end
|
2023-02-22 04:21:53 +13:00
|
|
|
|
|
|
|
describe "string join expression" do
|
|
|
|
test "no nil values" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{
|
|
|
|
full_name_with_nils: "Bill Jones",
|
|
|
|
full_name_with_nils_no_joiner: "BillJones"
|
|
|
|
} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:full_name_with_nils)
|
|
|
|
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "with nil value" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{
|
|
|
|
full_name_with_nils: "Bill",
|
|
|
|
full_name_with_nils_no_joiner: "Bill"
|
|
|
|
} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:full_name_with_nils)
|
|
|
|
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
end
|
2023-04-01 05:12:13 +13:00
|
|
|
|
2023-09-21 08:41:32 +12:00
|
|
|
test "arguments with cast_in_query?: false are not cast" do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "not", score: 42})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [post] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(similarity(search: expr(query(search: "match"))))
|
|
|
|
|> Api.read!()
|
|
|
|
|
|
|
|
assert post.title == "match"
|
|
|
|
end
|
|
|
|
|
2023-07-13 07:16:28 +12:00
|
|
|
describe "string split expression" do
|
|
|
|
test "with the default delimiter" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{
|
|
|
|
split_full_name: ["Bill", "Jones"]
|
|
|
|
} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load(:split_full_name)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "trimming whitespace" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill ",
|
|
|
|
last_name: "Jones ",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{
|
|
|
|
split_full_name_trim: ["Bill", "Jones"],
|
|
|
|
split_full_name: ["Bill", "Jones"]
|
|
|
|
} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load([:split_full_name_trim, :split_full_name])
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-30 11:17:32 +13:00
|
|
|
describe "count_nils/1" do
|
|
|
|
test "counts nil values" do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", "c"]})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [_] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(count_nils(list_containing_nils) == 2)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-18 14:35:46 +12:00
|
|
|
describe "-/1" do
|
|
|
|
test "makes numbers negative" do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{negative_score: -42}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:negative_score)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-23 06:51:31 +12:00
|
|
|
describe "maps" do
|
2023-11-20 13:46:29 +13:00
|
|
|
test "maps can reference filtered aggregates" do
|
2023-08-23 06:51:31 +12:00
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "foo", likes: 2})
|
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "foo", likes: 2})
|
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Comment
|
|
|
|
|> Ash.Changeset.new(%{title: "bar", likes: 2})
|
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{agg_map: %{called_foo: 2, called_bar: 1}}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:agg_map)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "maps can be constructed" do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{score_map: %{negative_score: %{foo: -42}}}] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:score_map)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-13 09:05:33 +12:00
|
|
|
describe "at/2" do
|
|
|
|
test "selects items by index" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill ",
|
|
|
|
last_name: "Jones ",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{
|
|
|
|
first_name_from_split: "Bill"
|
|
|
|
} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.filter(id == ^author.id)
|
|
|
|
|> Ash.Query.load([:first_name_from_split])
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-04-01 05:12:13 +13:00
|
|
|
test "dependent calc" do
|
|
|
|
post =
|
|
|
|
Post
|
2023-04-06 04:21:35 +12:00
|
|
|
|> Ash.Changeset.new(%{title: "match", price: 10_024})
|
2023-04-01 05:12:13 +13:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post.get_by_id(post.id,
|
|
|
|
query: Post |> Ash.Query.select([:id]) |> Ash.Query.load([:price_string_with_currency_sign])
|
|
|
|
)
|
|
|
|
end
|
2023-04-21 05:37:46 +12:00
|
|
|
|
2023-08-24 04:54:25 +12:00
|
|
|
test "nested get_path works" do
|
|
|
|
assert "thing" =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", price: 10_024, stuff: %{foo: %{bar: "thing"}}})
|
|
|
|
|> Ash.Changeset.deselect(:stuff)
|
|
|
|
|> Api.create!()
|
|
|
|
|> Api.load!(:foo_bar_from_stuff)
|
|
|
|
|> Map.get(:foo_bar_from_stuff)
|
|
|
|
end
|
|
|
|
|
2023-04-21 05:37:46 +12:00
|
|
|
test "runtime expression calcs" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
2023-04-22 04:43:58 +12:00
|
|
|
assert %AshPostgres.Test.Money{} =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.new(%{title: "match", price: 10_024})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Api.create!()
|
|
|
|
|> Api.load!(:calc_returning_json)
|
|
|
|
|> Map.get(:calc_returning_json)
|
|
|
|
|
|
|
|
assert [%AshPostgres.Test.Money{}] =
|
|
|
|
author
|
|
|
|
|> Api.load!(posts: :calc_returning_json)
|
|
|
|
|> Map.get(:posts)
|
|
|
|
|> Enum.map(&Map.get(&1, :calc_returning_json))
|
2023-04-21 05:37:46 +12:00
|
|
|
end
|
2023-11-17 12:12:00 +13:00
|
|
|
|
2023-12-22 13:52:29 +13:00
|
|
|
test "string_length and string_trim work" do
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{calculations: %{length: 9}} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.calculate(
|
|
|
|
:length,
|
|
|
|
expr(string_length(string_trim(first_name <> last_name <> " "))),
|
|
|
|
:integer
|
|
|
|
)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
2023-12-23 15:14:40 +13:00
|
|
|
|
2024-01-31 10:39:17 +13:00
|
|
|
test "an expression calculation that loads a runtime calculation works" do
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert [%{expr_referencing_runtime: "Bill Jones Bill Jones"}] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:expr_referencing_runtime)
|
|
|
|
|> Api.read!()
|
|
|
|
end
|
|
|
|
|
2023-12-23 15:14:40 +13:00
|
|
|
test "lazy values are evaluated lazily" do
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
|
|
first_name: "Bill",
|
|
|
|
last_name: "Jones",
|
|
|
|
bio: %{title: "Mr.", bio: "Bones"}
|
|
|
|
})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
assert %{calculations: %{string: "fred"}} =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.calculate(
|
|
|
|
:string,
|
|
|
|
expr(lazy({__MODULE__, :fred, []})),
|
|
|
|
:string
|
|
|
|
)
|
|
|
|
|> Api.read_one!()
|
|
|
|
end
|
|
|
|
|
|
|
|
def fred do
|
|
|
|
"fred"
|
|
|
|
end
|
2021-06-04 17:48:35 +12:00
|
|
|
end
|