mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
improvement: optimize not-in and fix dialyzer
This commit is contained in:
parent
60e0dad7b6
commit
a886fecfd6
4 changed files with 111 additions and 6 deletions
|
@ -1,4 +1,5 @@
|
||||||
defmodule Ash.Filter do
|
defmodule Ash.Filter do
|
||||||
|
@dialyzer {:nowarn_function, do_map: 2, map: 2}
|
||||||
alias Ash.Actions.SideLoad
|
alias Ash.Actions.SideLoad
|
||||||
alias Ash.Engine.Request
|
alias Ash.Engine.Request
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
defmodule Ash.Query.Expression do
|
defmodule Ash.Query.Expression do
|
||||||
@moduledoc "Represents a boolean expression"
|
@moduledoc "Represents a boolean expression"
|
||||||
|
@dialyzer {:nowarn_function, optimized_new: 4}
|
||||||
|
|
||||||
alias Ash.Query.Operator.{Eq, In}
|
alias Ash.Query.Operator.{Eq, In, NotEq}
|
||||||
alias Ash.Query.Ref
|
alias Ash.Query.Ref
|
||||||
|
|
||||||
defstruct [:op, :left, :right]
|
defstruct [:op, :left, :right]
|
||||||
|
@ -44,6 +45,10 @@ defmodule Ash.Query.Expression do
|
||||||
optimized_new(op, right, left, current_op)
|
optimized_new(op, right, left, current_op)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(op, %In{} = left, %NotEq{} = right, current_op) do
|
||||||
|
optimized_new(op, right, left, current_op)
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(op, %In{} = left, %Eq{} = right, current_op) do
|
def optimized_new(op, %In{} = left, %Eq{} = right, current_op) do
|
||||||
optimized_new(op, right, left, current_op)
|
optimized_new(op, right, left, current_op)
|
||||||
end
|
end
|
||||||
|
@ -56,6 +61,14 @@ defmodule Ash.Query.Expression do
|
||||||
do_new(op, left, right)
|
do_new(op, left, right)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(op, %NotEq{right: %Ref{}} = left, right, _) do
|
||||||
|
do_new(op, left, right)
|
||||||
|
end
|
||||||
|
|
||||||
|
def optimized_new(op, left, %NotEq{right: %Ref{}} = right, _) do
|
||||||
|
do_new(op, left, right)
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(
|
def optimized_new(
|
||||||
:or,
|
:or,
|
||||||
%Eq{left: left, right: value},
|
%Eq{left: left, right: value},
|
||||||
|
@ -65,6 +78,21 @@ defmodule Ash.Query.Expression do
|
||||||
%{right | right: MapSet.put(mapset, value)}
|
%{right | right: MapSet.put(mapset, value)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
:or,
|
||||||
|
%NotEq{left: left, right: value},
|
||||||
|
%In{left: left, right: %{__struct__: MapSet} = mapset} = right,
|
||||||
|
_
|
||||||
|
) do
|
||||||
|
without = MapSet.delete(mapset, value)
|
||||||
|
|
||||||
|
case MapSet.size(without) do
|
||||||
|
0 -> false
|
||||||
|
1 -> %Eq{left: left, right: Enum.at(without, 0)}
|
||||||
|
_ -> %{right | right: without}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(
|
def optimized_new(
|
||||||
:and,
|
:and,
|
||||||
%Eq{left: left, right: value} = left_expr,
|
%Eq{left: left, right: value} = left_expr,
|
||||||
|
@ -78,6 +106,28 @@ defmodule Ash.Query.Expression do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
:and,
|
||||||
|
%NotEq{left: left, right: value},
|
||||||
|
%In{left: left, right: %{__struct__: MapSet} = mapset} = right_expr,
|
||||||
|
_
|
||||||
|
) do
|
||||||
|
if MapSet.member?(mapset, value) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
right_expr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
op,
|
||||||
|
%NotEq{} = left,
|
||||||
|
%Eq{} = right,
|
||||||
|
current_op
|
||||||
|
) do
|
||||||
|
optimized_new(op, right, left, current_op)
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(
|
def optimized_new(
|
||||||
:or,
|
:or,
|
||||||
%Eq{left: left, right: left_value},
|
%Eq{left: left, right: left_value},
|
||||||
|
@ -87,6 +137,26 @@ defmodule Ash.Query.Expression do
|
||||||
%In{left: left, right: MapSet.new([left_value, right_value])}
|
%In{left: left, right: MapSet.new([left_value, right_value])}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
:or,
|
||||||
|
%NotEq{left: left, right: left_value},
|
||||||
|
%Eq{left: left, right: right_value} = right_expr,
|
||||||
|
_
|
||||||
|
)
|
||||||
|
when left_value != right_value do
|
||||||
|
right_expr
|
||||||
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
:or,
|
||||||
|
%NotEq{left: left, right: left_value},
|
||||||
|
%Eq{left: left, right: right_value},
|
||||||
|
_
|
||||||
|
)
|
||||||
|
when left_value == right_value do
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(
|
def optimized_new(
|
||||||
:and,
|
:and,
|
||||||
%Eq{left: left, right: left_value} = left_expr,
|
%Eq{left: left, right: left_value} = left_expr,
|
||||||
|
@ -100,6 +170,16 @@ defmodule Ash.Query.Expression do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def optimized_new(
|
||||||
|
:and,
|
||||||
|
%NotEq{left: left, right: left_value} = left_expr,
|
||||||
|
%NotEq{left: left, right: right_value},
|
||||||
|
_
|
||||||
|
)
|
||||||
|
when left_value == right_value do
|
||||||
|
left_expr
|
||||||
|
end
|
||||||
|
|
||||||
def optimized_new(
|
def optimized_new(
|
||||||
:or,
|
:or,
|
||||||
%In{left: left, right: left_values},
|
%In{left: left, right: left_values},
|
||||||
|
@ -164,18 +244,36 @@ defmodule Ash.Query.Expression do
|
||||||
do_new(op, left, right)
|
do_new(op, left, right)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp simplify?(%NotEq{} = left, %In{} = right), do: simplify?(right, left)
|
||||||
|
defp simplify?(%NotEq{} = left, %Eq{} = right), do: simplify?(right, left)
|
||||||
defp simplify?(%Eq{} = left, %In{} = right), do: simplify?(right, left)
|
defp simplify?(%Eq{} = left, %In{} = right), do: simplify?(right, left)
|
||||||
|
|
||||||
defp simplify?(%Eq{right: %Ref{}}, _), do: false
|
defp simplify?(%Eq{right: %Ref{}}, _), do: false
|
||||||
defp simplify?(_, %Eq{right: %Ref{}}), do: false
|
defp simplify?(_, %Eq{right: %Ref{}}), do: false
|
||||||
defp simplify?(%Eq{left: left}, %Eq{left: left}), do: true
|
defp simplify?(%Eq{left: left}, %Eq{left: left}), do: true
|
||||||
|
|
||||||
|
defp simplify?(%NotEq{right: %Ref{}}, _), do: false
|
||||||
|
defp simplify?(_, %NotEq{right: %Ref{}}), do: false
|
||||||
|
defp simplify?(%NotEq{left: left}, %NotEq{left: left}), do: true
|
||||||
|
|
||||||
defp simplify?(
|
defp simplify?(
|
||||||
%Eq{left: left},
|
%Eq{left: left},
|
||||||
%In{left: left, right: %MapSet{}}
|
%In{left: left, right: %MapSet{}}
|
||||||
),
|
),
|
||||||
do: true
|
do: true
|
||||||
|
|
||||||
|
defp simplify?(
|
||||||
|
%NotEq{left: left},
|
||||||
|
%In{left: left, right: %MapSet{}}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
defp simplify?(
|
||||||
|
%Eq{left: left},
|
||||||
|
%NotEq{left: left, right: %MapSet{}}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
defp simplify?(_, _), do: false
|
defp simplify?(_, _), do: false
|
||||||
|
|
||||||
defp do_new(op, left, right) do
|
defp do_new(op, left, right) do
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Ash.Query.Operator.In do
|
||||||
types: [[{:ref, :any}, {:array, :same}], [{:array, :same}, {:ref, :any}]]
|
types: [[{:ref, :any}, {:array, :same}], [{:array, :same}, {:ref, :any}]]
|
||||||
|
|
||||||
@inspect_items_limit 10
|
@inspect_items_limit 10
|
||||||
|
@dialyzer {:nowarn_function, compare: 2}
|
||||||
|
|
||||||
def new(_, []), do: {:known, false}
|
def new(_, []), do: {:known, false}
|
||||||
|
|
||||||
|
@ -46,8 +47,10 @@ defmodule Ash.Query.Operator.In do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def compare(%__MODULE__{}, %Ash.Query.Operator.Eq{right: %Ref{}}),
|
def compare(%__MODULE__{}, %Ash.Query.Operator.Eq{
|
||||||
do: false
|
right: %Ref{}
|
||||||
|
}),
|
||||||
|
do: false
|
||||||
|
|
||||||
def compare(%__MODULE__{left: left, right: %MapSet{} = left_right}, %Ash.Query.Operator.Eq{
|
def compare(%__MODULE__{left: left, right: %MapSet{} = left_right}, %Ash.Query.Operator.Eq{
|
||||||
left: left,
|
left: left,
|
||||||
|
|
|
@ -4,6 +4,8 @@ defmodule Ash.SatSolver do
|
||||||
alias Ash.Filter
|
alias Ash.Filter
|
||||||
alias Ash.Query.{Expression, Not, Ref}
|
alias Ash.Query.{Expression, Not, Ref}
|
||||||
|
|
||||||
|
@dialyzer {:nowarn_function, overlap?: 2}
|
||||||
|
|
||||||
defmacro b(statement) do
|
defmacro b(statement) do
|
||||||
value =
|
value =
|
||||||
Macro.prewalk(
|
Macro.prewalk(
|
||||||
|
@ -374,6 +376,7 @@ defmodule Ash.SatSolver do
|
||||||
|
|
||||||
def fully_simplify(expression) do
|
def fully_simplify(expression) do
|
||||||
expression
|
expression
|
||||||
|
|> do_fully_simplify()
|
||||||
|> lift_equals_out_of_in()
|
|> lift_equals_out_of_in()
|
||||||
|> do_fully_simplify()
|
|> do_fully_simplify()
|
||||||
end
|
end
|
||||||
|
@ -478,8 +481,8 @@ defmodule Ash.SatSolver do
|
||||||
def split_in_expressions(other, _), do: other
|
def split_in_expressions(other, _), do: other
|
||||||
|
|
||||||
def overlap?(
|
def overlap?(
|
||||||
%Ash.Query.Operator.In{left: left, right: %MapSet{} = left_right},
|
%Ash.Query.Operator.In{left: left, right: %{__struct__: MapSet} = left_right},
|
||||||
%Ash.Query.Operator.In{left: left, right: %MapSet{} = right_right}
|
%Ash.Query.Operator.In{left: left, right: %{__struct__: MapSet} = right_right}
|
||||||
) do
|
) do
|
||||||
if MapSet.equal?(left_right, right_right) do
|
if MapSet.equal?(left_right, right_right) do
|
||||||
false
|
false
|
||||||
|
@ -506,7 +509,7 @@ defmodule Ash.SatSolver do
|
||||||
|
|
||||||
def overlap?(
|
def overlap?(
|
||||||
%Ash.Query.Operator.Eq{left: left, right: left_right},
|
%Ash.Query.Operator.Eq{left: left, right: left_right},
|
||||||
%Ash.Query.Operator.In{left: left, right: %MapSet{} = right_right}
|
%Ash.Query.Operator.In{left: left, right: %{__struct__: MapSet} = right_right}
|
||||||
) do
|
) do
|
||||||
MapSet.member?(right_right, left_right)
|
MapSet.member?(right_right, left_right)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue