improvement: optimize relates_to_actor_via checks

This commit is contained in:
Zach Daniel 2022-09-28 20:37:19 -04:00
parent 6c94e5e578
commit 0e13456eb0
6 changed files with 89 additions and 2 deletions

View file

@ -391,10 +391,18 @@ defmodule Ash.DataLayer.Ets do
kind: :count,
relationship_path: relationship_path,
query: query,
authorization_filter: authorization_filter,
name: name,
load: load
},
{:ok, record} ->
query =
if authorization_filter do
Ash.Query.do_filter(query, authorization_filter)
else
query
end
with {:ok, loaded_record} <- api.load(record, relationship_path),
related <- Ash.Filter.Runtime.get_related(loaded_record, relationship_path),
{:ok, filtered} <-

View file

@ -1225,7 +1225,7 @@ defmodule Ash.Filter do
case do_run_other_data_layer_filters(
expr,
api,
Ash.Resource.Info.related(resource, path),
Ash.Resource.Info.related(resource, at_path ++ path),
data
) do
{:ok, new_nested} ->

View file

@ -168,7 +168,7 @@ defmodule Ash.Filter.Runtime do
end
defp flatten_many_to_many(record, rel, values, rest) do
full_flattened = Enum.map(values, &flatten_relationships(&1, [rest]))
full_flattened = Enum.map(values, &flatten_relationships(&1, [rest])) |> List.flatten()
Enum.map(full_flattened, fn value ->
record

View file

@ -21,3 +21,71 @@ defmodule Ash.Policy.Check.RelatesToActorVia do
Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
end
end
# defmodule Ash.Policy.Check.RelatesToActorVia do
# @moduledoc false
# use Ash.Policy.FilterCheck
# require Ash.Expr
# @impl true
# def describe(opts) do
# path = Enum.join(opts[:relationship_path], ".")
# "record.#{path} == actor"
# end
# @impl true
# def filter(opts) do
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
# pkey =
# last_relationship.destination
# |> Ash.Resource.Info.primary_key()
# if to_many? do
# Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
# else
# put_in_path(opts[:relationship_path], Enum.map(pkey, &{&1, {:_actor, &1}}))
# end
# end
# @impl true
# def reject(opts) do
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
# pkey =
# last_relationship.destination
# |> Ash.Resource.Info.primary_key()
# if to_many? do
# Ash.Expr.expr(not exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
# else
# [
# or: [
# [not: filter(opts)],
# [put_in_path(opts[:relationship_path], Enum.map(pkey, &{:is_nil, &1}))]
# ]
# ]
# end
# end
# defp relationship_info(resource, path, to_many? \\ false)
# defp relationship_info(resource, [rel], to_many?) do
# rel = Ash.Resource.Info.relationship(resource, rel)
# {rel, to_many? || rel.cardinality == :many}
# end
# defp relationship_info(resource, [rel | rest], to_many?) do
# rel = Ash.Resource.Info.relationship(resource, rel)
# relationship_info(rel.destination, rest, to_many? || rel.cardinality == :many)
# end
# defp put_in_path([], value) do
# value
# end
# defp put_in_path([key | rest], value) do
# [{key, put_in_path(rest, value)}]
# end
# end

View file

@ -90,4 +90,11 @@ defmodule Ash.Test.Policy.ComplexTest do
|> Ash.Query.filter(comments.text == "comment by a friend of a friend on my post")
|> Api.read!(actor: me)
end
test "aggregates can be loaded", %{me: me} do
Post
|> Ash.Query.load(:count_of_comments)
|> Ash.Query.filter(count_of_comments == 10)
|> Api.read!(actor: me)
end
end

View file

@ -43,6 +43,10 @@ defmodule Ash.Test.Support.PolicyComplex.Post do
end
end
aggregates do
count :count_of_comments, :comments
end
code_interface do
define_for Ash.Test.Support.PolicyComplex.Api
define :create, args: [:text]