mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
improvement: support partial eager evaluation of functions
This commit is contained in:
parent
5635c311fe
commit
5d20af82c3
6 changed files with 72 additions and 18 deletions
|
@ -37,6 +37,7 @@ spark_locals_without_parens = [
|
|||
broadcast_type: 1,
|
||||
bypass: 1,
|
||||
bypass: 2,
|
||||
calculate: 2,
|
||||
calculate: 3,
|
||||
calculate: 4,
|
||||
calculation: 1,
|
||||
|
|
|
@ -661,7 +661,16 @@ defmodule Ash.Engine.Request do
|
|||
Map.take(request, keys)
|
||||
) do
|
||||
{:ok, new_filter} ->
|
||||
{:ok, %{request | query: %{request.query | filter: new_filter}}}
|
||||
case Ash.Filter.hydrate_refs(new_filter, %{
|
||||
resource: request.query.resource,
|
||||
public?: false
|
||||
}) do
|
||||
{:ok, result} ->
|
||||
{:ok, %{request | query: %{request.query | filter: result}}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
|
|
@ -2325,21 +2325,11 @@ defmodule Ash.Filter do
|
|||
function_module,
|
||||
args
|
||||
) do
|
||||
if is_boolean(function) do
|
||||
if is_nil(context.resource) ||
|
||||
Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
||||
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
||||
else
|
||||
if is_nil(context.resource) ||
|
||||
Ash.DataLayer.data_layer_can?(context.resource, {:filter_expr, function}) do
|
||||
{:ok, BooleanExpression.optimized_new(:and, expression, function)}
|
||||
else
|
||||
case function_module.evaluate(function) do
|
||||
{:known, result} ->
|
||||
{:ok, result}
|
||||
|
||||
_ ->
|
||||
{:error, "data layer does not support the function #{inspect(function)}"}
|
||||
end
|
||||
end
|
||||
{:error, "data layer does not support the function #{inspect(function)}"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3066,6 +3056,16 @@ defmodule Ash.Filter do
|
|||
end
|
||||
end
|
||||
|
||||
def do_hydrate_refs(%__MODULE__{expression: expression} = filter, context) do
|
||||
case do_hydrate_refs(expression, context) do
|
||||
{:ok, expression} ->
|
||||
{:ok, %{filter | expression: expression}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def do_hydrate_refs(list, context) when is_list(list) do
|
||||
list
|
||||
|> Enum.reduce_while({:ok, []}, fn val, {:ok, acc} ->
|
||||
|
|
|
@ -15,8 +15,11 @@ defmodule Ash.Query.Function do
|
|||
@callback args() :: [arg]
|
||||
@callback new(list(term)) :: {:ok, term} | {:error, String.t() | Exception.t()}
|
||||
@callback evaluate(func :: map) :: :unknown | {:known, term}
|
||||
@callback partial_evaluate(func) :: func when func: map
|
||||
@callback private?() :: boolean
|
||||
|
||||
@optional_callbacks partial_evaluate: 1
|
||||
|
||||
def new(mod, args) do
|
||||
args = List.wrap(args)
|
||||
|
||||
|
@ -45,10 +48,14 @@ defmodule Ash.Query.Function do
|
|||
case mod.new(casted) do
|
||||
{:ok, function} ->
|
||||
if Enum.any?(casted, &expr?/1) do
|
||||
{:ok, function}
|
||||
if function_exported?(mod, :partial_evaluate, 1) && match?(%^mod{}, function) do
|
||||
{:ok, mod.partial_evaluate(function)}
|
||||
else
|
||||
{:ok, function}
|
||||
end
|
||||
else
|
||||
case function do
|
||||
%mod{__predicate__?: _} ->
|
||||
%^mod{__predicate__?: _} ->
|
||||
if mod.eager_evaluate?() do
|
||||
case mod.evaluate(function) do
|
||||
{:known, result} ->
|
||||
|
|
|
@ -3,6 +3,7 @@ defmodule Ash.Query.Function.If do
|
|||
If predicate is truthy, then the second argument is returned, otherwise the third.
|
||||
"""
|
||||
use Ash.Query.Function, name: :if
|
||||
import Ash.Filter.TemplateHelpers, only: [expr?: 1]
|
||||
|
||||
def args, do: [[:boolean, :any], [:boolean, :any, :any]]
|
||||
|
||||
|
@ -38,4 +39,22 @@ defmodule Ash.Query.Function.If do
|
|||
do: {:known, when_false}
|
||||
|
||||
def evaluate(%{arguments: [_, when_true, _]}), do: {:known, when_true}
|
||||
|
||||
def partial_evaluate(%{arguments: [false, _, when_false]}),
|
||||
do: when_false
|
||||
|
||||
def partial_evaluate(%{arguments: [nil, _, when_false]}),
|
||||
do: when_false
|
||||
|
||||
def partial_evaluate(%{arguments: [condition, when_true, _]} = fun) do
|
||||
if expr?(condition) do
|
||||
fun
|
||||
else
|
||||
when_true
|
||||
end
|
||||
end
|
||||
|
||||
def partial_evaluate(other) do
|
||||
raise inspect(other)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2146,7 +2146,16 @@ defmodule Ash.Query do
|
|||
|
||||
case new_filter do
|
||||
{:ok, filter} ->
|
||||
%{query | filter: filter}
|
||||
case Ash.Filter.hydrate_refs(filter, %{
|
||||
resource: query.resource,
|
||||
public?: false
|
||||
}) do
|
||||
{:ok, result} ->
|
||||
%{query | filter: result}
|
||||
|
||||
{:error, error} ->
|
||||
add_error(query, :filter, error)
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
add_error(query, :filter, error)
|
||||
|
@ -2183,7 +2192,16 @@ defmodule Ash.Query do
|
|||
|
||||
case filter do
|
||||
{:ok, filter} ->
|
||||
Map.put(query, :filter, filter)
|
||||
case Ash.Filter.hydrate_refs(filter, %{
|
||||
resource: query.resource,
|
||||
public?: false
|
||||
}) do
|
||||
{:ok, result} ->
|
||||
%{query | filter: result}
|
||||
|
||||
{:error, error} ->
|
||||
add_error(query, :filter, error)
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
add_error(query, :filter, error)
|
||||
|
|
Loading…
Reference in a new issue