mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
improvement: don't eager load sort data
This commit is contained in:
parent
779420743e
commit
007e0fb081
10 changed files with 259 additions and 62 deletions
|
@ -750,7 +750,13 @@ defmodule Ash.Actions.Read do
|
||||||
end)
|
end)
|
||||||
|> Enum.concat(calc_selects)
|
|> Enum.concat(calc_selects)
|
||||||
|> Enum.concat(query_selects)
|
|> Enum.concat(query_selects)
|
||||||
|> remove_already_selected(request_opts[:initial_data])
|
|> then(fn fields ->
|
||||||
|
if request_opts[:lazy?] do
|
||||||
|
remove_already_selected(fields, request_opts[:initial_data])
|
||||||
|
else
|
||||||
|
fields
|
||||||
|
end
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
@ -1043,44 +1049,44 @@ defmodule Ash.Actions.Read do
|
||||||
defp loaded_query(query, calculations_at_runtime) do
|
defp loaded_query(query, calculations_at_runtime) do
|
||||||
query = load_calc_requirements(query, calculations_at_runtime)
|
query = load_calc_requirements(query, calculations_at_runtime)
|
||||||
|
|
||||||
query =
|
# query =
|
||||||
Enum.reduce(query.sort || [], query, fn
|
# Enum.reduce(query.sort || [], query, fn
|
||||||
{%Ash.Query.Calculation{name: name, module: module, opts: opts} = calculation, _},
|
# {%Ash.Query.Calculation{name: name, module: module, opts: opts} = calculation, _},
|
||||||
query ->
|
# query ->
|
||||||
{resource_load, resource_select} =
|
# {resource_load, resource_select} =
|
||||||
if resource_calculation = Ash.Resource.Info.calculation(query.resource, name) do
|
# if resource_calculation = Ash.Resource.Info.calculation(query.resource, name) do
|
||||||
{resource_calculation.load, resource_calculation.select}
|
# {resource_calculation.load, resource_calculation.select}
|
||||||
else
|
# else
|
||||||
{[], []}
|
# {[], []}
|
||||||
end
|
# end
|
||||||
|
|
||||||
fields_to_select =
|
# fields_to_select =
|
||||||
resource_select
|
# resource_select
|
||||||
|> Kernel.||([])
|
# |> Kernel.||([])
|
||||||
|> Enum.concat(module.select(query, opts, calculation.context) || [])
|
# |> Enum.concat(module.select(query, opts, calculation.context) || [])
|
||||||
|
|
||||||
calculation = %{calculation | load: name, select: fields_to_select}
|
# calculation = %{calculation | load: name, select: fields_to_select}
|
||||||
|
|
||||||
query =
|
# query =
|
||||||
Ash.Query.load(
|
# Ash.Query.load(
|
||||||
query,
|
# query,
|
||||||
module.load(
|
# module.load(
|
||||||
query,
|
# query,
|
||||||
opts,
|
# opts,
|
||||||
Map.put(calculation.context, :context, query.context)
|
# Map.put(calculation.context, :context, query.context)
|
||||||
)
|
# )
|
||||||
|> Ash.Actions.Helpers.validate_calculation_load!(module)
|
# |> Ash.Actions.Helpers.validate_calculation_load!(module)
|
||||||
)
|
# )
|
||||||
|
|
||||||
Ash.Query.load(query, resource_load)
|
# Ash.Query.load(query, resource_load)
|
||||||
|
|
||||||
{key, _value}, query ->
|
# {key, _value}, query ->
|
||||||
if Ash.Resource.Info.aggregate(query.resource, key) do
|
# if Ash.Resource.Info.aggregate(query.resource, key) do
|
||||||
Ash.Query.load(query, key)
|
# Ash.Query.load(query, key)
|
||||||
else
|
# else
|
||||||
query
|
# query
|
||||||
end
|
# end
|
||||||
end)
|
# end)
|
||||||
|
|
||||||
query.load
|
query.load
|
||||||
|> Enum.reduce(query, fn
|
|> Enum.reduce(query, fn
|
||||||
|
@ -1141,8 +1147,9 @@ defmodule Ash.Actions.Read do
|
||||||
load_calc_requirements(query, new_loads, new_loads ++ checked)
|
load_calc_requirements(query, new_loads, new_loads ++ checked)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp remove_already_selected(fields, %{results: results}),
|
defp remove_already_selected(fields, %struct{results: results})
|
||||||
do: remove_already_selected(fields, results)
|
when struct in [Ash.Page.Keyset, Ash.Page.Offset],
|
||||||
|
do: remove_already_selected(fields, results)
|
||||||
|
|
||||||
defp remove_already_selected(fields, record) when not is_list(record),
|
defp remove_already_selected(fields, record) when not is_list(record),
|
||||||
do: remove_already_selected(fields, List.wrap(record))
|
do: remove_already_selected(fields, List.wrap(record))
|
||||||
|
|
|
@ -243,20 +243,49 @@ defmodule Ash.Actions.Sort do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def runtime_sort([], _empty), do: []
|
@doc """
|
||||||
def runtime_sort(results, empty) when empty in [nil, []], do: results
|
Sort records at runtime
|
||||||
|
|
||||||
def runtime_sort([%resource{} | _] = results, [{field, direction}]) do
|
Opts
|
||||||
sort_by(results, &resolve_field(&1, field, resource), direction)
|
|
||||||
end
|
|
||||||
|
|
||||||
def runtime_sort([%resource{} | _] = results, [{field, direction} | rest]) do
|
* `:api` - The api to use if data needs to be loaded
|
||||||
|
* `:lazy?` - Wether to use already loaded values or to re-load them when necessary. Defaults to `false`
|
||||||
|
"""
|
||||||
|
def runtime_sort(results, sort, opts \\ [])
|
||||||
|
def runtime_sort([], _empty, _), do: []
|
||||||
|
def runtime_sort(results, empty, _) when empty in [nil, []], do: results
|
||||||
|
def runtime_sort([single_result], _, _), do: [single_result]
|
||||||
|
|
||||||
|
def runtime_sort([%resource{} | _] = results, [{field, direction} | rest], opts) do
|
||||||
results
|
results
|
||||||
|
|> load_field(field, resource, opts)
|
||||||
|> Enum.group_by(&resolve_field(&1, field, resource))
|
|> Enum.group_by(&resolve_field(&1, field, resource))
|
||||||
|> sort_by(fn {key, _value} -> key end, direction)
|
|> sort_by(fn {key, _value} -> key end, direction)
|
||||||
|> Enum.flat_map(fn {_, records} ->
|
|> Enum.flat_map(fn {_, records} ->
|
||||||
runtime_sort(records, rest)
|
runtime_sort(records, rest, Keyword.put(opts, :rekey?, false))
|
||||||
end)
|
end)
|
||||||
|
|> tap(fn new_results ->
|
||||||
|
if opts[:rekey?] do
|
||||||
|
Enum.map(new_results, fn new_result ->
|
||||||
|
Enum.find(results, fn result ->
|
||||||
|
resource.primary_key_matches?(new_result, result)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_field(records, field, resource, opts) do
|
||||||
|
if is_nil(opts[:api]) || (opts[:lazy?] && Ash.Resource.loaded?(records, field)) do
|
||||||
|
records
|
||||||
|
else
|
||||||
|
query =
|
||||||
|
resource
|
||||||
|
|> Ash.Query.load(field)
|
||||||
|
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
||||||
|
|
||||||
|
opts[:api].load!(records, query)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resolve_field(record, %Ash.Query.Calculation{} = calc, resource) do
|
defp resolve_field(record, %Ash.Query.Calculation{} = calc, resource) do
|
||||||
|
|
|
@ -337,7 +337,7 @@ defmodule Ash.DataLayer.Ets do
|
||||||
do_add_calculations(records, resource, calculations) do
|
do_add_calculations(records, resource, calculations) do
|
||||||
offset_records =
|
offset_records =
|
||||||
records
|
records
|
||||||
|> Sort.runtime_sort(sort)
|
|> Sort.runtime_sort(sort, api: api)
|
||||||
|> Enum.drop(offset || 0)
|
|> Enum.drop(offset || 0)
|
||||||
|
|
||||||
if limit do
|
if limit do
|
||||||
|
|
|
@ -219,7 +219,7 @@ defmodule Ash.DataLayer.Mnesia do
|
||||||
{:ok, filtered} ->
|
{:ok, filtered} ->
|
||||||
offset_records =
|
offset_records =
|
||||||
filtered
|
filtered
|
||||||
|> Sort.runtime_sort(sort)
|
|> Sort.runtime_sort(sort, api: api)
|
||||||
|> Enum.drop(offset || 0)
|
|> Enum.drop(offset || 0)
|
||||||
|
|
||||||
limited_records =
|
limited_records =
|
||||||
|
|
|
@ -61,7 +61,7 @@ defmodule Ash.DataLayer.Simple do
|
||||||
{:ok, results} ->
|
{:ok, results} ->
|
||||||
{:ok,
|
{:ok,
|
||||||
results
|
results
|
||||||
|> Ash.Actions.Sort.runtime_sort(sort)
|
|> Ash.Actions.Sort.runtime_sort(sort, api: api)
|
||||||
|> then(fn data ->
|
|> then(fn data ->
|
||||||
if limit do
|
if limit do
|
||||||
Enum.take(data, limit)
|
Enum.take(data, limit)
|
||||||
|
|
|
@ -289,7 +289,7 @@ defmodule Ash.EmbeddableType do
|
||||||
def apply_constraints(nil, _), do: {:ok, nil}
|
def apply_constraints(nil, _), do: {:ok, nil}
|
||||||
|
|
||||||
def apply_constraints(term, constraints) do
|
def apply_constraints(term, constraints) do
|
||||||
ShadowApi.load(term, constraints[:load] || [])
|
ShadowApi.load(term, constraints[:load] || [], lazy?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_change(nil, new_value, _constraints) do
|
def handle_change(nil, new_value, _constraints) do
|
||||||
|
|
|
@ -36,12 +36,30 @@ defmodule Ash.Filter.Runtime do
|
||||||
resource
|
resource
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{refs_to_load, refs} =
|
||||||
|
filter
|
||||||
|
|> Ash.Filter.list_refs()
|
||||||
|
|> Enum.reject(&match?(%{attribute: %Ash.Resource.Attribute{}}, &1))
|
||||||
|
|> Enum.split_with(&(&1.relationship_path == []))
|
||||||
|
|
||||||
|
refs_to_load =
|
||||||
|
refs_to_load
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{attribute: %Ash.Resource.Calculation{load: nil} = calc} ->
|
||||||
|
{calc.name, calc}
|
||||||
|
|
||||||
|
%{attribute: %{name: name}} ->
|
||||||
|
name
|
||||||
|
end)
|
||||||
|
|
||||||
|
records = api.load!(records, refs_to_load)
|
||||||
|
|
||||||
filter
|
filter
|
||||||
|> Ash.Filter.relationship_paths(true)
|
|> Ash.Filter.relationship_paths(true)
|
||||||
|> Enum.reject(&(&1 == []))
|
|> Enum.reject(&(&1 == []))
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.reject(&Ash.Resource.loaded?(records, &1))
|
|> Enum.reject(&Ash.Resource.loaded?(records, &1))
|
||||||
|> Enum.map(&path_to_load(resource, &1))
|
|> Enum.map(&path_to_load(resource, &1, refs))
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
Enum.reduce_while(records, {:ok, []}, fn record, {:ok, records} ->
|
Enum.reduce_while(records, {:ok, []}, fn record, {:ok, records} ->
|
||||||
|
@ -85,20 +103,24 @@ defmodule Ash.Filter.Runtime do
|
||||||
def load_parent_requirements(api, expression, %resource{} = parent) do
|
def load_parent_requirements(api, expression, %resource{} = parent) do
|
||||||
expression
|
expression
|
||||||
|> Ash.Filter.flat_map(fn %Ash.Query.Parent{expr: expr} ->
|
|> Ash.Filter.flat_map(fn %Ash.Query.Parent{expr: expr} ->
|
||||||
expr
|
Ash.Filter.list_refs(expr)
|
||||||
|> Ash.Filter.relationship_paths(true)
|
|
||||||
|> Enum.reject(&(&1 == []))
|
|
||||||
end)
|
end)
|
||||||
|
|> Enum.reject(&match?(%{attribute: %Ash.Resource.Attribute{}}, &1))
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.reject(&Ash.Resource.loaded?(parent, &1))
|
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
{:ok, parent}
|
{:ok, parent}
|
||||||
|
|
||||||
requirements ->
|
refs ->
|
||||||
|
to_load =
|
||||||
|
refs
|
||||||
|
|> Enum.map(& &1.relationship_path)
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.map(&path_to_load(resource, &1, refs))
|
||||||
|
|
||||||
query =
|
query =
|
||||||
resource
|
resource
|
||||||
|> Ash.Query.load(requirements)
|
|> Ash.Query.load(to_load)
|
||||||
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
||||||
|
|
||||||
api.load(parent, query)
|
api.load(parent, query)
|
||||||
|
@ -134,7 +156,7 @@ defmodule Ash.Filter.Runtime do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
need_to_load ->
|
need_to_load ->
|
||||||
{:load, Enum.map(need_to_load, &path_to_load(resource, &1))}
|
{:load, Enum.map(need_to_load, &path_to_load(resource, &1, []))}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -626,14 +648,53 @@ defmodule Ash.Filter.Runtime do
|
||||||
|
|
||||||
defp or_default(other, _), do: other
|
defp or_default(other, _), do: other
|
||||||
|
|
||||||
defp path_to_load(resource, [first]) do
|
defp path_to_load(resource, [first], refs) do
|
||||||
related = Ash.Resource.Info.related(resource, first)
|
to_load =
|
||||||
{first, Ash.Query.set_context(related, %{private: %{internal?: true}})}
|
refs
|
||||||
|
|> Enum.filter(&(&1.relationship_path == []))
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{attribute: %Ash.Resource.Calculation{load: nil} = calc} ->
|
||||||
|
{calc.name, calc}
|
||||||
|
|
||||||
|
%{attribute: %{name: name}} ->
|
||||||
|
name
|
||||||
|
end)
|
||||||
|
|
||||||
|
query =
|
||||||
|
resource
|
||||||
|
|> Ash.Resource.Info.related(first)
|
||||||
|
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
||||||
|
|> Ash.Query.load(to_load)
|
||||||
|
|
||||||
|
{first, query}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp path_to_load(resource, [first | rest]) do
|
defp path_to_load(resource, [first | rest], refs) do
|
||||||
related = Ash.Resource.Info.related(resource, first)
|
related = Ash.Resource.Info.related(resource, first)
|
||||||
{first, [path_to_load(related, rest)]}
|
|
||||||
|
to_load =
|
||||||
|
refs
|
||||||
|
|> Enum.filter(&(&1.relationship_path == []))
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{attribute: %Ash.Resource.Calculation{load: nil} = calc} ->
|
||||||
|
{calc.name, calc}
|
||||||
|
|
||||||
|
%{attribute: %{name: name}} ->
|
||||||
|
name
|
||||||
|
end)
|
||||||
|
|
||||||
|
further_refs =
|
||||||
|
refs
|
||||||
|
|> Enum.filter(fn ref ->
|
||||||
|
ref.relationship_path
|
||||||
|
|> Enum.at(0)
|
||||||
|
|> Kernel.==(first)
|
||||||
|
end)
|
||||||
|
|> Enum.map(fn ref ->
|
||||||
|
%{ref | relationship_path: Enum.drop(ref.relationship_path, 1)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{first, [path_to_load(related, rest, further_refs)] ++ to_load}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp expression_matches(:and, left, right, record, parent) do
|
defp expression_matches(:and, left, right, record, parent) do
|
||||||
|
|
|
@ -834,7 +834,14 @@ defmodule Ash.Query do
|
||||||
```
|
```
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec load(t() | Ash.Resource.t(), atom | list(atom) | Keyword.t()) :: t()
|
@spec load(
|
||||||
|
t() | Ash.Resource.t(),
|
||||||
|
atom
|
||||||
|
| Ash.Query.Calculation.t()
|
||||||
|
| list(atom | Ash.Query.Calculation.t())
|
||||||
|
| list({atom | Ash.Query.Calculation.t(), term})
|
||||||
|
) ::
|
||||||
|
t()
|
||||||
def load(query, fields) when not is_list(fields) do
|
def load(query, fields) when not is_list(fields) do
|
||||||
load(query, List.wrap(fields))
|
load(query, List.wrap(fields))
|
||||||
end
|
end
|
||||||
|
@ -905,6 +912,33 @@ defmodule Ash.Query do
|
||||||
|
|
||||||
defp do_load(query, field) do
|
defp do_load(query, field) do
|
||||||
cond do
|
cond do
|
||||||
|
match?(%Ash.Query.Calculation{}, field) ->
|
||||||
|
calculation = field
|
||||||
|
|
||||||
|
fields_to_select =
|
||||||
|
calculation.module.select(query, calculation.opts, calculation.context)
|
||||||
|
|> Kernel.||([])
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.filter(&Ash.Resource.Info.attribute(query.resource, &1))
|
||||||
|
|
||||||
|
loads =
|
||||||
|
calculation.module.load(
|
||||||
|
query,
|
||||||
|
calculation.opts,
|
||||||
|
Map.put(calculation.context, :context, query.context)
|
||||||
|
)
|
||||||
|
|> Ash.Actions.Helpers.validate_calculation_load!(calculation.module)
|
||||||
|
|> Enum.reject(&Ash.Resource.Info.attribute(query.resource, &1))
|
||||||
|
|
||||||
|
calculation = %{
|
||||||
|
calculation
|
||||||
|
| load: field,
|
||||||
|
required_loads: loads,
|
||||||
|
select: fields_to_select
|
||||||
|
}
|
||||||
|
|
||||||
|
Map.update!(query, :calculations, &Map.put(&1, calculation.name, calculation))
|
||||||
|
|
||||||
Ash.Resource.Info.attribute(query.resource, field) ->
|
Ash.Resource.Info.attribute(query.resource, field) ->
|
||||||
Ash.Query.ensure_selected(query, field)
|
Ash.Query.ensure_selected(query, field)
|
||||||
|
|
||||||
|
|
|
@ -231,5 +231,5 @@ defmodule Ash.Sort do
|
||||||
collation that affects their sorting, making it unpredictable from the perspective
|
collation that affects their sorting, making it unpredictable from the perspective
|
||||||
of a tool using the database: https://www.postgresql.org/docs/current/collation.html
|
of a tool using the database: https://www.postgresql.org/docs/current/collation.html
|
||||||
"""
|
"""
|
||||||
defdelegate runtime_sort(results, sort), to: Ash.Actions.Sort
|
defdelegate runtime_sort(results, sort, api \\ nil), to: Ash.Actions.Sort
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@ defmodule Ash.Test.CalculationTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
require Ash.Query
|
||||||
|
|
||||||
defmodule Concat do
|
defmodule Concat do
|
||||||
# An example concatenation calculation, that accepts the delimiter as an argument
|
# An example concatenation calculation, that accepts the delimiter as an argument
|
||||||
use Ash.Calculation
|
use Ash.Calculation
|
||||||
|
@ -66,6 +68,32 @@ defmodule Ash.Test.CalculationTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule NamesOfBestFriendsOfMe do
|
||||||
|
use Ash.Calculation
|
||||||
|
|
||||||
|
def load(_query, _opts, args) do
|
||||||
|
if args[:only_special] do
|
||||||
|
query =
|
||||||
|
__MODULE__.User
|
||||||
|
|> Ash.Query.filter(special == true)
|
||||||
|
|> Ash.Query.ensure_selected(:full_name)
|
||||||
|
|
||||||
|
[best_friends_of_me: query]
|
||||||
|
else
|
||||||
|
[best_friends_of_me: :full_name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def calculate(records, _opts, _) do
|
||||||
|
Enum.map(records, fn record ->
|
||||||
|
record.best_friends_of_me
|
||||||
|
|> Enum.map(& &1.full_name)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> Enum.join(" - ")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defmodule BestFriendsFirstNamePlusStuff do
|
defmodule BestFriendsFirstNamePlusStuff do
|
||||||
use Ash.Calculation
|
use Ash.Calculation
|
||||||
|
|
||||||
|
@ -96,6 +124,7 @@ defmodule Ash.Test.CalculationTest do
|
||||||
uuid_primary_key :id
|
uuid_primary_key :id
|
||||||
attribute :first_name, :string
|
attribute :first_name, :string
|
||||||
attribute :last_name, :string
|
attribute :last_name, :string
|
||||||
|
attribute :special, :boolean
|
||||||
end
|
end
|
||||||
|
|
||||||
calculations do
|
calculations do
|
||||||
|
@ -130,6 +159,8 @@ defmodule Ash.Test.CalculationTest do
|
||||||
|
|
||||||
calculate :best_friends_name, :string, BestFriendsName
|
calculate :best_friends_name, :string, BestFriendsName
|
||||||
|
|
||||||
|
calculate :names_of_best_friends_of_me, :string, NamesOfBestFriendsOfMe
|
||||||
|
|
||||||
calculate :conditional_full_name,
|
calculate :conditional_full_name,
|
||||||
:string,
|
:string,
|
||||||
expr(
|
expr(
|
||||||
|
@ -172,6 +203,10 @@ defmodule Ash.Test.CalculationTest do
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :best_friend, __MODULE__
|
belongs_to :best_friend, __MODULE__
|
||||||
|
|
||||||
|
has_many :best_friends_of_me, __MODULE__ do
|
||||||
|
destination_attribute :best_friend_id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -428,4 +463,35 @@ defmodule Ash.Test.CalculationTest do
|
||||||
|
|
||||||
assert full_names == ["bob", "brian cranston", "zach daniel"]
|
assert full_names == ["bob", "brian cranston", "zach daniel"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# test "loading calculations with different relationship dependencies won't collide", %{
|
||||||
|
# user1: %{id: user1_id} = user1
|
||||||
|
# } do
|
||||||
|
# user3 =
|
||||||
|
# User
|
||||||
|
# |> Ash.Changeset.new(%{first_name: "chidi", last_name: "anagonye", special: true})
|
||||||
|
# |> Ash.Changeset.manage_relationship(:best_friend, user1, type: :append_and_remove)
|
||||||
|
# |> Api.create!()
|
||||||
|
|
||||||
|
# assert %{
|
||||||
|
# calculations: %{
|
||||||
|
# names_of_best_friends_of_me: "brian cranston - chidi anagonye",
|
||||||
|
# names_of_special_best_friends_of_me: "chidi anagonye"
|
||||||
|
# }
|
||||||
|
# } =
|
||||||
|
# User
|
||||||
|
# |> Ash.Query.filter(id == ^user1_id)
|
||||||
|
# |> Ash.Query.load_calculation_as(
|
||||||
|
# :names_of_best_friends_of_me,
|
||||||
|
# :names_of_special_best_friends_of_me,
|
||||||
|
# %{
|
||||||
|
# special: true
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# |> Ash.Query.load_calculation_as(
|
||||||
|
# :names_of_best_friends_of_me,
|
||||||
|
# :names_of_best_friends_of_me
|
||||||
|
# )
|
||||||
|
# |> Api.read_one!()
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue