mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-20 21:43:12 +12:00
87 lines
2.5 KiB
Elixir
87 lines
2.5 KiB
Elixir
defmodule AshPostgres.Functions.TrigramSimilarity do
|
|
@moduledoc """
|
|
A filter predicate that filters based on trigram similarity.
|
|
|
|
See the postgres docs on [https://www.postgresql.org/docs/9.6/pgtrgm.html](trigram) for more information.
|
|
|
|
Requires the pg_trgm extension. Configure which extensions you have installed in your `AshPostgres.Repo`
|
|
|
|
At least one of the `equals`, `greater_than` and `less_than`
|
|
|
|
# Example
|
|
|
|
filter(query, [trigram_similarity: [:name, "geoff", [greater_than: 0.4]]])
|
|
"""
|
|
|
|
use Ash.Query.Function, name: :trigram_similarity
|
|
|
|
def args, do: [:ref, :term, {:options, [:less_than, :greater_than, :equals]}]
|
|
|
|
def new([_, text | _]) when not is_binary(text) do
|
|
{:error, "#{Ash.Query.Function.ordinal(2)} argument must be a string, got #{text}"}
|
|
end
|
|
|
|
def new([%Ref{} = ref, text, opts]) do
|
|
with :ok <- required_options_provided(opts),
|
|
{:ok, less_than} <- validate_similarity(opts[:less_than]),
|
|
{:ok, greater_than} <- validate_similarity(opts[:greater_than]),
|
|
{:ok, equals} <- validate_similarity(opts[:equals]) do
|
|
new_options = [
|
|
less_than: less_than,
|
|
greater_than: greater_than,
|
|
equals: equals
|
|
]
|
|
|
|
{:ok,
|
|
%__MODULE__{
|
|
arguments: [
|
|
ref,
|
|
text,
|
|
new_options
|
|
]
|
|
}}
|
|
else
|
|
_ ->
|
|
{:error,
|
|
"Invalid options for `trigram_similarity` in the #{Ash.Query.Function.ordinal(3)} argument"}
|
|
end
|
|
end
|
|
|
|
def compare(%__MODULE__{arguments: [ref]}, %Ash.Query.Operator.IsNil{left: ref}) do
|
|
:mutually_exclusive
|
|
end
|
|
|
|
def compare(_, _), do: :unknown
|
|
|
|
defp validate_similarity(nil), do: {:ok, nil}
|
|
defp validate_similarity(1), do: {:ok, 1.0}
|
|
defp validate_similarity(0), do: {:ok, 0.0}
|
|
|
|
defp validate_similarity(similarity)
|
|
when is_float(similarity) and similarity <= 1.0 and similarity >= 0.0 do
|
|
{:ok, similarity}
|
|
end
|
|
|
|
defp validate_similarity(similarity) when is_binary(similarity) do
|
|
sanitized =
|
|
case similarity do
|
|
"." <> decimal_part -> "0." <> decimal_part
|
|
other -> other
|
|
end
|
|
|
|
case Float.parse(sanitized) do
|
|
{float, ""} -> {:ok, float}
|
|
_ -> :error
|
|
end
|
|
end
|
|
|
|
defp required_options_provided(opts) do
|
|
case {opts[:greater_than], opts[:less_than], opts[:equals]} do
|
|
{nil, nil, nil} -> :error
|
|
{nil, nil, _equals} -> :ok
|
|
{_greater_than, nil, nil} -> :ok
|
|
{nil, _less_than, nil} -> :ok
|
|
{_greater_than, _less_than, nil} -> :ok
|
|
end
|
|
end
|
|
end
|