2020-06-30 09:20:29 +12:00
|
|
|
defmodule Ash.Test.Filter.FilterInteractionTest do
|
|
|
|
use ExUnit.Case, async: false
|
|
|
|
|
2021-04-19 11:41:49 +12:00
|
|
|
import Ash.Changeset
|
2020-07-01 09:29:43 +12:00
|
|
|
import ExUnit.CaptureLog
|
2022-06-22 13:00:47 +12:00
|
|
|
import Ash.Test
|
2020-07-01 09:29:43 +12:00
|
|
|
|
2020-06-30 09:42:01 +12:00
|
|
|
alias Ash.DataLayer.Mnesia
|
|
|
|
|
2020-10-08 18:22:55 +13:00
|
|
|
require Ash.Query
|
|
|
|
|
2020-06-30 09:20:29 +12:00
|
|
|
defmodule Profile do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Ets
|
|
|
|
|
|
|
|
actions do
|
2022-04-29 10:07:06 +12:00
|
|
|
defaults [:create, :read, :update, :destroy]
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2021-01-13 09:40:55 +13:00
|
|
|
uuid_primary_key :id
|
2020-07-15 17:38:42 +12:00
|
|
|
attribute(:bio, :string)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2020-07-15 17:38:42 +12:00
|
|
|
belongs_to(:user, Ash.Test.Filter.FilterInteractionTest.User)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule User do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Ets
|
|
|
|
|
|
|
|
actions do
|
2022-04-29 10:07:06 +12:00
|
|
|
defaults [:create, :read, :update, :destroy]
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2021-01-13 09:40:55 +13:00
|
|
|
uuid_primary_key :id
|
2020-07-15 17:38:42 +12:00
|
|
|
attribute(:name, :string)
|
|
|
|
attribute(:allow_second_author, :boolean)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2022-08-16 06:00:02 +12:00
|
|
|
has_many(:posts, Ash.Test.Filter.FilterInteractionTest.Post,
|
|
|
|
destination_attribute: :author_id
|
|
|
|
)
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2020-07-15 17:38:42 +12:00
|
|
|
has_many(:second_posts, Ash.Test.Filter.FilterInteractionTest.Post,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute: :author_id
|
2020-07-15 17:38:42 +12:00
|
|
|
)
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2022-08-16 06:00:02 +12:00
|
|
|
has_one(:profile, Profile, destination_attribute: :user_id)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule PostLink do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Mnesia
|
|
|
|
|
|
|
|
actions do
|
2022-04-29 10:07:06 +12:00
|
|
|
defaults [:create, :read, :update, :destroy]
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2021-01-13 09:40:55 +13:00
|
|
|
belongs_to(:source_post, Ash.Test.Filter.FilterInteractionTest.Post,
|
|
|
|
primary_key?: true,
|
|
|
|
required?: true
|
|
|
|
)
|
2020-07-15 17:38:42 +12:00
|
|
|
|
2021-01-13 09:40:55 +13:00
|
|
|
belongs_to(:destination_post, Ash.Test.Filter.FilterInteractionTest.Post,
|
|
|
|
primary_key?: true,
|
|
|
|
required?: true
|
|
|
|
)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule Post do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Mnesia
|
|
|
|
|
|
|
|
actions do
|
2022-04-29 10:07:06 +12:00
|
|
|
defaults [:create, :read, :update, :destroy]
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2021-01-13 09:40:55 +13:00
|
|
|
uuid_primary_key :id
|
2020-07-15 17:38:42 +12:00
|
|
|
attribute(:title, :string)
|
|
|
|
attribute(:contents, :string)
|
|
|
|
attribute(:points, :integer)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2020-07-15 17:38:42 +12:00
|
|
|
belongs_to(:author, User,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute: :id,
|
|
|
|
source_attribute: :author_id
|
2020-07-15 17:38:42 +12:00
|
|
|
)
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2020-07-15 17:38:42 +12:00
|
|
|
many_to_many(:related_posts, __MODULE__,
|
2020-06-30 09:20:29 +12:00
|
|
|
through: PostLink,
|
2022-08-16 06:00:02 +12:00
|
|
|
source_attribute_on_join_resource: :source_post_id,
|
|
|
|
destination_attribute_on_join_resource: :destination_post_id
|
2020-07-15 17:38:42 +12:00
|
|
|
)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-07 19:41:02 +13:00
|
|
|
defmodule Registry do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Registry
|
|
|
|
|
|
|
|
entries do
|
|
|
|
entry(Post)
|
|
|
|
entry(User)
|
|
|
|
entry(Profile)
|
|
|
|
entry(PostLink)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-06-30 09:20:29 +12:00
|
|
|
defmodule Api do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Api
|
|
|
|
|
|
|
|
resources do
|
2021-10-07 19:41:02 +13:00
|
|
|
registry Registry
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
setup do
|
2020-07-01 09:29:43 +12:00
|
|
|
capture_log(fn ->
|
|
|
|
Mnesia.start(Api)
|
|
|
|
end)
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
on_exit(fn ->
|
2020-07-01 09:29:43 +12:00
|
|
|
capture_log(fn ->
|
|
|
|
:mnesia.stop()
|
|
|
|
:mnesia.delete_schema([node()])
|
|
|
|
end)
|
2020-06-30 09:20:29 +12:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "mnesia data layer sanity test" do
|
2020-07-12 18:25:53 +12:00
|
|
|
post =
|
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "best"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> Api.create!()
|
2022-06-22 13:00:47 +12:00
|
|
|
|> strip_metadata()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
assert [^post] = strip_metadata(Api.read!(Post))
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2020-07-15 17:38:42 +12:00
|
|
|
post |> new(%{title: "worst"}) |> Api.update!()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
new_post = %{post | title: "worst"}
|
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
assert [^new_post] = strip_metadata(Api.read!(Post))
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
Api.destroy!(post)
|
|
|
|
|
|
|
|
assert [] = Api.read!(Post)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "cross data layer filtering" do
|
|
|
|
test "it properly filters with a simple filter" do
|
2022-06-22 13:00:47 +12:00
|
|
|
author =
|
|
|
|
User
|
|
|
|
|> new(%{name: "best author"})
|
|
|
|
|> Api.create!()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
post1 =
|
|
|
|
Post
|
|
|
|
|> new(%{title: "best"})
|
|
|
|
|> replace_relationship(:author, author)
|
|
|
|
|> Api.create!()
|
2021-01-22 09:21:58 +13:00
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
post1_id = post1.id
|
2022-03-16 10:41:25 +13:00
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
Post
|
|
|
|
|> new(%{title: "worst"})
|
|
|
|
|> Api.create!()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
query =
|
|
|
|
Post
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(author.name == "best author")
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
assert [%{id: ^post1_id}] = Api.read!(query)
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "parallelizable filtering of related resources with a data layer that cannot join" do
|
2020-07-12 18:25:53 +12:00
|
|
|
post2 =
|
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "two"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "three"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> Api.create!()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
post1 =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "one"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> replace_relationship(:related_posts, [post2])
|
|
|
|
|> Api.create!()
|
2020-06-30 09:20:29 +12:00
|
|
|
|
|
|
|
query =
|
|
|
|
Post
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(related_posts.title == "two")
|
2020-06-30 09:20:29 +12:00
|
|
|
|
2021-01-23 10:41:32 +13:00
|
|
|
post1_id = post1.id
|
2021-01-22 09:21:58 +13:00
|
|
|
|
2021-01-23 10:41:32 +13:00
|
|
|
assert [%{id: ^post1_id}] = Api.read!(query)
|
2021-01-22 09:21:58 +13:00
|
|
|
end
|
|
|
|
|
2021-05-10 09:24:48 +12:00
|
|
|
test "parallelizable filter with filtered loads" do
|
2020-07-12 18:25:53 +12:00
|
|
|
post2 =
|
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "two"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "three"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> Api.create!()
|
2020-06-30 17:19:20 +12:00
|
|
|
|
|
|
|
post1 =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2020-07-15 17:38:42 +12:00
|
|
|
|> new(%{title: "one"})
|
2020-07-12 18:25:53 +12:00
|
|
|
|> replace_relationship(:related_posts, [post2, post3])
|
|
|
|
|> Api.create!()
|
2020-06-30 17:19:20 +12:00
|
|
|
|
2022-03-16 10:41:25 +13:00
|
|
|
post2
|
|
|
|
|> Api.load!(:related_posts)
|
|
|
|
|
2020-06-30 17:19:20 +12:00
|
|
|
posts_query =
|
|
|
|
Post
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(title == "three")
|
2020-06-30 17:19:20 +12:00
|
|
|
|
|
|
|
query =
|
|
|
|
Post
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(related_posts.title == "two")
|
2020-08-26 16:16:08 +12:00
|
|
|
|> Ash.Query.load(related_posts: posts_query)
|
2020-06-30 17:19:20 +12:00
|
|
|
|
|
|
|
post1_id = post1.id
|
|
|
|
|
|
|
|
post3_id = post3.id
|
|
|
|
|
|
|
|
assert [%{id: ^post1_id, related_posts: [%{id: ^post3_id}]}] = Api.read!(query)
|
|
|
|
end
|
2022-09-07 10:02:01 +12:00
|
|
|
|
|
|
|
test "exists/2 in the same data layer" do
|
|
|
|
post2 =
|
|
|
|
Post
|
|
|
|
|> new(%{title: "two"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
|
|
|
|> new(%{title: "three"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
|
|
|
|> new(%{title: "one"})
|
|
|
|
|> replace_relationship(:related_posts, [post2, post3])
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> new(%{title: "four"})
|
|
|
|
|> replace_relationship(:related_posts, [post3])
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post2
|
|
|
|
|> Api.load!(:related_posts)
|
|
|
|
|
|
|
|
query =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.filter(exists(related_posts, title == "two"))
|
|
|
|
|
|
|
|
post1_id = post1.id
|
|
|
|
|
|
|
|
assert [%{id: ^post1_id}] = Api.read!(query)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "exists/2 across data layers" do
|
|
|
|
author =
|
|
|
|
User
|
|
|
|
|> new(%{name: "best author"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
author2 =
|
|
|
|
User
|
|
|
|
|> new(%{name: "worst author"})
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
|
|
|
|> new(%{title: "best"})
|
|
|
|
|> replace_relationship(:author, author)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
post1_id = post1.id
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> new(%{title: "worst"})
|
|
|
|
|> replace_relationship(:author, author2)
|
|
|
|
|> Api.create!()
|
|
|
|
|
|
|
|
query = Ash.Query.filter(Post, exists(author, contains(name, "best")))
|
|
|
|
|
|
|
|
assert [%{id: ^post1_id}] = Api.read!(query)
|
|
|
|
end
|
2020-06-30 09:20:29 +12:00
|
|
|
end
|
|
|
|
end
|