mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
fix: various filter & expression fixes
This commit is contained in:
parent
936dac8627
commit
a234f0b6bf
11 changed files with 121 additions and 70 deletions
|
@ -274,7 +274,7 @@ defmodule Ash.Actions.Sort do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, expression} ->
|
{:ok, expression} ->
|
||||||
case Ash.Expr.eval(expression, record: record) do
|
case Ash.Expr.eval_hydrated(expression, record: record) do
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
{:ok, value}
|
{:ok, value}
|
||||||
|
|
||||||
|
|
|
@ -355,7 +355,7 @@ defmodule Ash.DataLayer.Ets do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, expression} ->
|
{:ok, expression} ->
|
||||||
case Ash.Expr.eval(expression, record: record) do
|
case Ash.Expr.eval_hydrated(expression, record: record) do
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
if calculation.load do
|
if calculation.load do
|
||||||
{:cont, {:ok, Map.put(record, calculation.load, value)}}
|
{:cont, {:ok, Map.put(record, calculation.load, value)}}
|
||||||
|
|
|
@ -5,7 +5,36 @@ defmodule Ash.Expr do
|
||||||
@type t :: any
|
@type t :: any
|
||||||
@pass_through_funcs [:where, :or_where, :expr, :@]
|
@pass_through_funcs [:where, :or_where, :expr, :@]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Evaluate an expression. See `eval/2` for more.
|
||||||
|
"""
|
||||||
|
def eval!(expression, opts \\ []) do
|
||||||
|
case eval(expression, opts) do
|
||||||
|
{:ok, result} ->
|
||||||
|
result
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
raise Ash.Error.to_ash_error(error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Evaluate an expression. This function only works if you have no references, or if you provide the `record` option.
|
||||||
|
"""
|
||||||
def eval(expression, opts \\ []) do
|
def eval(expression, opts \\ []) do
|
||||||
|
expression
|
||||||
|
|> Ash.Filter.hydrate_refs(%{})
|
||||||
|
|> case do
|
||||||
|
{:ok, hydrated} ->
|
||||||
|
eval_hydrated(hydrated, opts)
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def eval_hydrated(expression, opts \\ []) do
|
||||||
Ash.Filter.Runtime.do_match(opts[:record], expression, opts[:parent])
|
Ash.Filter.Runtime.do_match(opts[:record], expression, opts[:parent])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -890,7 +890,7 @@ defmodule Ash.Filter do
|
||||||
|> Enum.flat_map(fn calculation ->
|
|> Enum.flat_map(fn calculation ->
|
||||||
expression = calculation.module.expression(calculation.opts, calculation.context)
|
expression = calculation.module.expression(calculation.opts, calculation.context)
|
||||||
|
|
||||||
case Ash.Filter.hydrate_refs(expression, %{
|
case hydrate_refs(expression, %{
|
||||||
resource: resource,
|
resource: resource,
|
||||||
aggregates: aggregates,
|
aggregates: aggregates,
|
||||||
calculations: calculations,
|
calculations: calculations,
|
||||||
|
@ -1979,33 +1979,43 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp attribute(%{public?: true, resource: resource}, attribute),
|
defp attribute(%{public?: true, resource: resource}, attribute) when not is_nil(resource),
|
||||||
do: Ash.Resource.Info.public_attribute(resource, attribute)
|
do: Ash.Resource.Info.public_attribute(resource, attribute)
|
||||||
|
|
||||||
defp attribute(%{public?: false, resource: resource}, attribute) do
|
defp attribute(%{public?: false, resource: resource}, attribute) when not is_nil(resource) do
|
||||||
Ash.Resource.Info.attribute(resource, attribute)
|
Ash.Resource.Info.attribute(resource, attribute)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp aggregate(%{public?: true, resource: resource}, aggregate),
|
defp attribute(_, _), do: nil
|
||||||
|
|
||||||
|
defp aggregate(%{public?: true, resource: resource}, aggregate) when not is_nil(resource),
|
||||||
do: Ash.Resource.Info.public_aggregate(resource, aggregate)
|
do: Ash.Resource.Info.public_aggregate(resource, aggregate)
|
||||||
|
|
||||||
defp aggregate(%{public?: false, resource: resource}, aggregate),
|
defp aggregate(%{public?: false, resource: resource}, aggregate) when not is_nil(resource),
|
||||||
do: Ash.Resource.Info.aggregate(resource, aggregate)
|
do: Ash.Resource.Info.aggregate(resource, aggregate)
|
||||||
|
|
||||||
defp calculation(%{public?: true, resource: resource}, calculation),
|
defp aggregate(_, _), do: nil
|
||||||
|
|
||||||
|
defp calculation(%{public?: true, resource: resource}, calculation) when not is_nil(resource),
|
||||||
do: Ash.Resource.Info.public_calculation(resource, calculation)
|
do: Ash.Resource.Info.public_calculation(resource, calculation)
|
||||||
|
|
||||||
defp calculation(%{public?: false, resource: resource}, calculation),
|
defp calculation(%{public?: false, resource: resource}, calculation) when not is_nil(resource),
|
||||||
do: Ash.Resource.Info.calculation(resource, calculation)
|
do: Ash.Resource.Info.calculation(resource, calculation)
|
||||||
|
|
||||||
defp relationship(%{public?: true, resource: resource}, relationship) do
|
defp calculation(_, _), do: nil
|
||||||
|
|
||||||
|
defp relationship(%{public?: true, resource: resource}, relationship)
|
||||||
|
when not is_nil(resource) do
|
||||||
Ash.Resource.Info.public_relationship(resource, relationship)
|
Ash.Resource.Info.public_relationship(resource, relationship)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp relationship(%{public?: false, resource: resource}, relationship) do
|
defp relationship(%{public?: false, resource: resource}, relationship)
|
||||||
|
when not is_nil(resource) do
|
||||||
Ash.Resource.Info.relationship(resource, relationship)
|
Ash.Resource.Info.relationship(resource, relationship)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp relationship(_, _), do: nil
|
||||||
|
|
||||||
defp related(context, relationship) when not is_list(relationship) do
|
defp related(context, relationship) when not is_list(relationship) do
|
||||||
related(context, [relationship])
|
related(context, [relationship])
|
||||||
end
|
end
|
||||||
|
@ -2262,10 +2272,17 @@ defmodule Ash.Filter do
|
||||||
if is_boolean(function) do
|
if is_boolean(function) do
|
||||||
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
||||||
else
|
else
|
||||||
if Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
if context.resource &&
|
||||||
|
Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
||||||
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
||||||
else
|
else
|
||||||
{:error, "data layer does not support the function #{inspect(function)}"}
|
case function_module.evaluate(function) do
|
||||||
|
{:known, result} ->
|
||||||
|
{:ok, result}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "data layer does not support the function #{inspect(function)}"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2725,10 +2742,17 @@ defmodule Ash.Filter do
|
||||||
if is_boolean(function) do
|
if is_boolean(function) do
|
||||||
{:ok, function}
|
{:ok, function}
|
||||||
else
|
else
|
||||||
if Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
if context.resource &&
|
||||||
|
Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
||||||
{:ok, function}
|
{:ok, function}
|
||||||
else
|
else
|
||||||
{:error, "data layer does not support the function #{inspect(function)}"}
|
case function_module.evaluate(function) do
|
||||||
|
{:known, result} ->
|
||||||
|
{:ok, result}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "data layer does not support the function #{inspect(function)}"}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -2767,10 +2791,18 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs({key, value}, context) when is_atom(key) do
|
def hydrate_refs(value, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
context =
|
||||||
|
context
|
||||||
|
|> Map.put_new(:resource, nil)
|
||||||
|
|> Map.put_new(:root_resource, context[:resource])
|
||||||
|
|> Map.put_new(:public?, false)
|
||||||
|
|
||||||
case hydrate_refs(value, context) do
|
do_hydrate_refs(value, context)
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_hydrate_refs({key, value}, context) when is_atom(key) do
|
||||||
|
case do_hydrate_refs(value, context) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
{:ok, {key, hydrated}}
|
{:ok, {key, hydrated}}
|
||||||
|
|
||||||
|
@ -2779,13 +2811,11 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(
|
def do_hydrate_refs(
|
||||||
%Ref{attribute: attribute} = ref,
|
%Ref{attribute: attribute} = ref,
|
||||||
%{aggregates: aggregates, calculations: calculations} = context
|
%{aggregates: aggregates, calculations: calculations} = context
|
||||||
)
|
)
|
||||||
when is_atom(attribute) do
|
when is_atom(attribute) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
|
||||||
|
|
||||||
case related(context, ref.relationship_path) do
|
case related(context, ref.relationship_path) do
|
||||||
nil ->
|
nil ->
|
||||||
{:error,
|
{:error,
|
||||||
|
@ -2876,16 +2906,13 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%Ref{relationship_path: relationship_path, resource: nil} = ref, context) do
|
def do_hydrate_refs(%Ref{relationship_path: relationship_path, resource: nil} = ref, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
|
||||||
{:ok, %{ref | resource: Ash.Resource.Info.related(context.resource, relationship_path)}}
|
{:ok, %{ref | resource: Ash.Resource.Info.related(context.resource, relationship_path)}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%BooleanExpression{left: left, right: right} = expr, context) do
|
def do_hydrate_refs(%BooleanExpression{left: left, right: right} = expr, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
with {:ok, left} <- do_hydrate_refs(left, context),
|
||||||
|
{:ok, right} <- do_hydrate_refs(right, context) do
|
||||||
with {:ok, left} <- hydrate_refs(left, context),
|
|
||||||
{:ok, right} <- hydrate_refs(right, context) do
|
|
||||||
{:ok, %{expr | left: left, right: right}}
|
{:ok, %{expr | left: left, right: right}}
|
||||||
else
|
else
|
||||||
other ->
|
other ->
|
||||||
|
@ -2893,24 +2920,19 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%Not{expression: expression} = expr, context) do
|
def do_hydrate_refs(%Not{expression: expression} = expr, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
with {:ok, expression} <- do_hydrate_refs(expression, context) do
|
||||||
|
|
||||||
with {:ok, expression} <- hydrate_refs(expression, context) do
|
|
||||||
{:ok, %{expr | expression: expression}}
|
{:ok, %{expr | expression: expression}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%Call{} = call, context) do
|
def do_hydrate_refs(%Call{} = call, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
|
||||||
resolve_call(call, context)
|
resolve_call(call, context)
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%{__predicate__?: _, left: left, right: right} = expr, context) do
|
def do_hydrate_refs(%{__predicate__?: _, left: left, right: right} = expr, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
with {:ok, left} <- do_hydrate_refs(left, context),
|
||||||
|
{:ok, right} <- do_hydrate_refs(right, context) do
|
||||||
with {:ok, left} <- hydrate_refs(left, context),
|
|
||||||
{:ok, right} <- hydrate_refs(right, context) do
|
|
||||||
{:ok, %{expr | left: left, right: right}}
|
{:ok, %{expr | left: left, right: right}}
|
||||||
else
|
else
|
||||||
other ->
|
other ->
|
||||||
|
@ -2918,10 +2940,8 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%{__predicate__?: _, arguments: arguments} = expr, context) do
|
def do_hydrate_refs(%{__predicate__?: _, arguments: arguments} = expr, context) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
case do_hydrate_refs(arguments, context) do
|
||||||
|
|
||||||
case hydrate_refs(arguments, context) do
|
|
||||||
{:ok, args} ->
|
{:ok, args} ->
|
||||||
{:ok, %{expr | arguments: args}}
|
{:ok, %{expr | arguments: args}}
|
||||||
|
|
||||||
|
@ -2930,7 +2950,7 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%Ash.Query.Parent{expr: expr} = this, context) do
|
def do_hydrate_refs(%Ash.Query.Parent{expr: expr} = this, context) do
|
||||||
context =
|
context =
|
||||||
%{
|
%{
|
||||||
context
|
context
|
||||||
|
@ -2940,7 +2960,7 @@ defmodule Ash.Filter do
|
||||||
}
|
}
|
||||||
|> Map.put(:relationship_path, [])
|
|> Map.put(:relationship_path, [])
|
||||||
|
|
||||||
case hydrate_refs(expr, context) do
|
case do_hydrate_refs(expr, context) do
|
||||||
{:ok, expr} ->
|
{:ok, expr} ->
|
||||||
{:ok, %{this | expr: expr}}
|
{:ok, %{this | expr: expr}}
|
||||||
|
|
||||||
|
@ -2949,7 +2969,10 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(%Ash.Query.Exists{expr: expr, at_path: at_path, path: path} = exists, context) do
|
def do_hydrate_refs(
|
||||||
|
%Ash.Query.Exists{expr: expr, at_path: at_path, path: path} = exists,
|
||||||
|
context
|
||||||
|
) do
|
||||||
new_resource = Ash.Resource.Info.related(context[:resource], at_path ++ path)
|
new_resource = Ash.Resource.Info.related(context[:resource], at_path ++ path)
|
||||||
|
|
||||||
context = %{
|
context = %{
|
||||||
|
@ -2963,7 +2986,7 @@ defmodule Ash.Filter do
|
||||||
data_layer: Ash.DataLayer.data_layer(new_resource)
|
data_layer: Ash.DataLayer.data_layer(new_resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
case hydrate_refs(expr, context) do
|
case do_hydrate_refs(expr, context) do
|
||||||
{:ok, expr} ->
|
{:ok, expr} ->
|
||||||
{:ok, %{exists | expr: expr}}
|
{:ok, %{exists | expr: expr}}
|
||||||
|
|
||||||
|
@ -2972,12 +2995,10 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(list, context) when is_list(list) do
|
def do_hydrate_refs(list, context) when is_list(list) do
|
||||||
context = Map.put_new(context, :root_resource, context[:resource])
|
|
||||||
|
|
||||||
list
|
list
|
||||||
|> Enum.reduce_while({:ok, []}, fn val, {:ok, acc} ->
|
|> Enum.reduce_while({:ok, []}, fn val, {:ok, acc} ->
|
||||||
case hydrate_refs(val, context) do
|
case do_hydrate_refs(val, context) do
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
{:cont, {:ok, [value | acc]}}
|
{:cont, {:ok, [value | acc]}}
|
||||||
|
|
||||||
|
@ -2991,7 +3012,7 @@ defmodule Ash.Filter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hydrate_refs(val, _context) do
|
def do_hydrate_refs(val, _context) do
|
||||||
{:ok, val}
|
{:ok, val}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -461,8 +461,9 @@ defmodule Ash.Filter.Runtime do
|
||||||
defp resolve_ref(%Ash.Query.Ref{attribute: attribute}, nil, _),
|
defp resolve_ref(%Ash.Query.Ref{attribute: attribute}, nil, _),
|
||||||
do: :unknown |> or_default(attribute)
|
do: :unknown |> or_default(attribute)
|
||||||
|
|
||||||
defp resolve_ref(_, nil, _),
|
defp resolve_ref(_ref, nil, _) do
|
||||||
do: :unknown
|
:unknown
|
||||||
|
end
|
||||||
|
|
||||||
defp resolve_ref(
|
defp resolve_ref(
|
||||||
%Ash.Query.Ref{
|
%Ash.Query.Ref{
|
||||||
|
|
|
@ -320,7 +320,7 @@ defmodule Ash.Flow do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
case Ash.Expr.eval(hydrated) do
|
case Ash.Expr.eval_hydrated(hydrated) do
|
||||||
{:ok, result} ->
|
{:ok, result} ->
|
||||||
result
|
result
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ defmodule Ash.Policy.FilterCheck do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:halt, {:error, error}}
|
{:halt, {:error, error}}
|
||||||
|
@ -107,7 +107,7 @@ defmodule Ash.Policy.FilterCheck do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:error, error}
|
{:error, error}
|
||||||
|
@ -135,7 +135,7 @@ defmodule Ash.Policy.FilterCheck do
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
Ash.Expr.eval(hydrated, record: data)
|
Ash.Expr.eval_hydrated(hydrated, record: data)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:halt, {:error, error}}
|
{:halt, {:error, error}}
|
||||||
|
@ -150,7 +150,7 @@ defmodule Ash.Policy.FilterCheck do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:halt, {:error, error}}
|
{:halt, {:error, error}}
|
||||||
|
|
|
@ -73,7 +73,7 @@ defmodule Ash.Policy.FilterCheckWithContext do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:error, error}
|
{:error, error}
|
||||||
|
@ -91,7 +91,7 @@ defmodule Ash.Policy.FilterCheckWithContext do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:error, error}
|
{:error, error}
|
||||||
|
@ -119,7 +119,7 @@ defmodule Ash.Policy.FilterCheckWithContext do
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
Ash.Expr.eval(hydrated, record: data)
|
Ash.Expr.eval_hydrated(hydrated, record: data)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:error, error}
|
{:error, error}
|
||||||
|
@ -134,7 +134,7 @@ defmodule Ash.Policy.FilterCheckWithContext do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, hydrated} ->
|
{:ok, hydrated} ->
|
||||||
Ash.Expr.eval(hydrated)
|
Ash.Expr.eval_hydrated(hydrated)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
{:error, error}
|
{:error, error}
|
||||||
|
|
|
@ -112,10 +112,10 @@ defmodule Ash.Query.Operator.Basic do
|
||||||
defp do_evaluate(op, left, right) do
|
defp do_evaluate(op, left, right) do
|
||||||
if Decimal.is_decimal(left) || Decimal.is_decimal(right) do
|
if Decimal.is_decimal(left) || Decimal.is_decimal(right) do
|
||||||
case op do
|
case op do
|
||||||
:+ -> Decimal.add(to_decimal(left), to_decimal(right))
|
:+ -> {:known, Decimal.add(to_decimal(left), to_decimal(right))}
|
||||||
:* -> Decimal.mult(to_decimal(left), to_decimal(right))
|
:* -> {:known, Decimal.mult(to_decimal(left), to_decimal(right))}
|
||||||
:- -> Decimal.sub(to_decimal(left), to_decimal(right))
|
:- -> {:known, Decimal.sub(to_decimal(left), to_decimal(right))}
|
||||||
:/ -> Decimal.div(to_decimal(left), to_decimal(right))
|
:/ -> {:known, Decimal.div(to_decimal(left), to_decimal(right))}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
{:known, apply(Kernel, unquote(opts[:symbol]), [left, right])}
|
{:known, apply(Kernel, unquote(opts[:symbol]), [left, right])}
|
||||||
|
|
|
@ -2007,6 +2007,7 @@ defmodule Ash.Query do
|
||||||
),
|
),
|
||||||
{:ok, query} <-
|
{:ok, query} <-
|
||||||
add_tenant(query, ash_query),
|
add_tenant(query, ash_query),
|
||||||
|
{:ok, query} <- Ash.DataLayer.select(query, ash_query.select, ash_query.resource),
|
||||||
{:ok, query} <-
|
{:ok, query} <-
|
||||||
add_aggregates(query, ash_query, aggregates),
|
add_aggregates(query, ash_query, aggregates),
|
||||||
{:ok, query} <-
|
{:ok, query} <-
|
||||||
|
@ -2016,8 +2017,7 @@ defmodule Ash.Query do
|
||||||
{:ok, query} <-
|
{:ok, query} <-
|
||||||
Ash.DataLayer.limit(query, ash_query.limit, resource),
|
Ash.DataLayer.limit(query, ash_query.limit, resource),
|
||||||
{:ok, query} <-
|
{:ok, query} <-
|
||||||
Ash.DataLayer.offset(query, ash_query.offset, resource),
|
Ash.DataLayer.offset(query, ash_query.offset, resource) do
|
||||||
{:ok, query} <- Ash.DataLayer.select(query, ash_query.select, ash_query.resource) do
|
|
||||||
if opts[:no_modify?] || !ash_query.action || !ash_query.action.modify_query do
|
if opts[:no_modify?] || !ash_query.action || !ash_query.action.modify_query do
|
||||||
{:ok, query}
|
{:ok, query}
|
||||||
else
|
else
|
||||||
|
|
|
@ -23,7 +23,7 @@ defmodule Ash.Resource.Calculation.Expression do
|
||||||
public?: false
|
public?: false
|
||||||
}) do
|
}) do
|
||||||
{:ok, expression} ->
|
{:ok, expression} ->
|
||||||
case Ash.Expr.eval(expression, record: record) do
|
case Ash.Expr.eval_hydrated(expression, record: record) do
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
{:cont, {:ok, [value | values]}}
|
{:cont, {:ok, [value | values]}}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue