mirror of
https://github.com/ash-project/ash.git
synced 2024-09-21 05:53:06 +12:00
4fc53baf5f
improvement: percolate `nil` values in operators in ash expresion language (like SQL) chore: more docs work
122 lines
3.2 KiB
Elixir
122 lines
3.2 KiB
Elixir
defmodule Ash.Query.Operator.LessThan do
|
|
@moduledoc """
|
|
left < right
|
|
|
|
Does not simplify, but is used as the simplification value for
|
|
`Ash.Query.Operator.LessThanOrEqual`, `Ash.Query.Operator.GreaterThan` and
|
|
`Ash.Query.Operator.GreaterThanOrEqual`.
|
|
|
|
When comparing predicates, it is mutually exclusive with `Ash.Query.Operator.IsNil`.
|
|
Additionally, it compares as mutually inclusive with any `Ash.Query.Operator.Eq` and
|
|
any `Ash.Query.Operator.LessThan` who's right sides are less than it, and mutually
|
|
exclusive with any `Ash.Query.Operator.Eq` or `Ash.Query.Operator.GreaterThan` who's
|
|
right side's are greater than or equal to it.
|
|
"""
|
|
|
|
use Ash.Query.Operator,
|
|
operator: :<,
|
|
name: :less_than,
|
|
predicate?: true,
|
|
types: [:same, :any]
|
|
|
|
alias Ash.Query.Operator.{Eq, IsNil}
|
|
|
|
def evaluate(%{left: nil}), do: :unknown
|
|
def evaluate(%{right: nil}), do: :unknown
|
|
|
|
def evaluate(%{left: left, right: right}) do
|
|
{:known, Comp.less_than?(left, right)}
|
|
end
|
|
|
|
def bulk_compare(all_predicates) do
|
|
all_predicates
|
|
|> Enum.group_by(& &1.left)
|
|
|> Enum.flat_map(fn {_, all_predicates} ->
|
|
predicates =
|
|
all_predicates
|
|
|> Enum.filter(&(&1.__struct__ in [__MODULE__, Eq]))
|
|
|> Enum.sort_by(& &1.right)
|
|
|
|
nil_exclusive(all_predicates) ++
|
|
inclusive_values(predicates) ++ exclusive_values(predicates)
|
|
end)
|
|
end
|
|
|
|
defp inclusive_values(sorted_predicates, acc \\ [])
|
|
|
|
defp inclusive_values([], acc), do: acc
|
|
|
|
defp inclusive_values([%Eq{} = first | rest], acc) do
|
|
rest
|
|
|> Enum.reject(&(&1.right == first.right))
|
|
|> Enum.filter(&(&1.__struct__ == __MODULE__))
|
|
|> case do
|
|
[] ->
|
|
inclusive_values(rest, acc)
|
|
|
|
other ->
|
|
new_acc =
|
|
other
|
|
|> Enum.map(&Ash.SatSolver.left_implies_right(first, &1))
|
|
|> Kernel.++(acc)
|
|
|
|
inclusive_values(rest, new_acc)
|
|
end
|
|
end
|
|
|
|
defp inclusive_values([%__MODULE__{} = first | rest], acc) do
|
|
rest
|
|
|> Enum.reject(&(&1.right == first.right))
|
|
|> Enum.filter(&(&1.__struct__ == Eq))
|
|
|> case do
|
|
[] ->
|
|
inclusive_values(rest, acc)
|
|
|
|
other ->
|
|
new_acc =
|
|
other
|
|
|> Enum.map(&Ash.SatSolver.right_implies_left(first, &1))
|
|
|> Kernel.++(acc)
|
|
|
|
inclusive_values(rest, new_acc)
|
|
end
|
|
end
|
|
|
|
defp exclusive_values(sorted_predicates, acc \\ [])
|
|
defp exclusive_values([], acc), do: acc
|
|
|
|
defp exclusive_values([%__MODULE__{} = first | rest], acc) do
|
|
case Enum.filter(rest, &(&1.__struct__ == Eq)) do
|
|
[] ->
|
|
exclusive_values(rest, acc)
|
|
|
|
other ->
|
|
new_acc =
|
|
other
|
|
|> Enum.map(&Ash.SatSolver.left_excludes_right(first, &1))
|
|
|> Kernel.++(acc)
|
|
|
|
exclusive_values(rest, new_acc)
|
|
end
|
|
end
|
|
|
|
defp exclusive_values([_ | rest], acc) do
|
|
exclusive_values(rest, acc)
|
|
end
|
|
|
|
defp nil_exclusive(predicates) do
|
|
is_nils = Enum.filter(predicates, &(&1.__struct__ == IsNil))
|
|
|
|
case is_nils do
|
|
[] ->
|
|
[]
|
|
|
|
is_nils ->
|
|
predicates
|
|
|> Enum.filter(&(&1.__struct__ == __MODULE__))
|
|
|> Enum.flat_map(fn lt ->
|
|
Ash.SatSolver.mutually_exclusive([lt | is_nils])
|
|
end)
|
|
end
|
|
end
|
|
end
|