Current section
Files
Jump to
Current section
Files
lib/aerospike/exp/list.ex
defmodule Aerospike.Exp.List do
@moduledoc """
List CDT expression helpers.
These builders create expressions that evaluate list operations against a
list expression, usually `Aerospike.Exp.list_bin/1`.
"""
import Bitwise
alias Aerospike.Exp
alias Aerospike.Exp.Module
alias Aerospike.Op.List, as: ListOp
alias Aerospike.PolicyInteger
@typedoc "Opaque server-side expression."
@type t :: Exp.t()
@typedoc """
List selector return type.
"""
@type return_type :: ListOp.return_type()
@typedoc """
List expression write policy accepted in `opts[:policy]`.
Append/increment operations expect both `:order` and `:flags`; insert and set
operations encode only `:flags`.
"""
@type policy :: ListOp.policy()
@typedoc """
Common list expression options.
Supported keys:
* `:policy` - list write policy for modify expressions that document it.
* `:return_type` - selector return type such as `:value`, `:count`, or
`[:value, :inverted]`.
"""
@type opts :: [policy: policy(), return_type: return_type()]
@append 1
@append_items 2
@insert 3
@insert_items 4
@set 9
@clear 11
@increment 12
@sort 13
@size 16
@get_by_index 19
@get_by_rank 21
@get_by_value 22
@get_by_value_list 23
@get_by_index_range 24
@get_by_value_interval 25
@get_by_rank_range 26
@get_by_value_rel_rank_range 27
@remove_by_index 32
@remove_by_rank 34
@remove_by_value 35
@remove_by_value_list 36
@remove_by_index_range 37
@remove_by_value_interval 38
@remove_by_rank_range 39
@remove_by_value_rel_rank_range 40
@inverted 0x10000
@doc "Return no result for a selector expression."
@spec return_none() :: 0
def return_none, do: ListOp.return_none()
@doc "Return selected item indexes."
@spec return_index() :: 1
def return_index, do: ListOp.return_index()
@doc "Return selected reverse indexes, counted from the list end."
@spec return_reverse_index() :: 2
def return_reverse_index, do: ListOp.return_reverse_index()
@doc "Return selected value ranks."
@spec return_rank() :: 3
def return_rank, do: ListOp.return_rank()
@doc "Return selected reverse ranks, counted from the highest value."
@spec return_reverse_rank() :: 4
def return_reverse_rank, do: ListOp.return_reverse_rank()
@doc "Return the selected item count."
@spec return_count() :: 5
def return_count, do: ListOp.return_count()
@doc "Return selected value or values."
@spec return_value() :: 7
def return_value, do: ListOp.return_value()
@doc "Return whether matching values exist."
@spec return_exists() :: 13
def return_exists, do: ListOp.return_exists()
@doc "Invert the selector so it applies outside the matched range."
@spec return_inverted() :: 0x10000
def return_inverted, do: @inverted
@doc "Appends `value` to a list expression. Supports `policy:`."
@spec append(Exp.t(), Exp.t(), opts()) :: t()
def append(%Exp{} = bin, %Exp{} = value, opts \\ []) do
modify(bin, @append, [value | list_policy(opts)])
end
@doc "Appends list expression `values` to a list expression. Supports `policy:`."
@spec append_items(Exp.t(), Exp.t(), opts()) :: t()
def append_items(%Exp{} = bin, %Exp{} = values, opts \\ []) do
modify(bin, @append_items, [values | list_policy(opts)])
end
@doc "Inserts `value` at `index`. Supports `policy: [flags: flags]`."
@spec insert(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def insert(%Exp{} = bin, %Exp{} = index, %Exp{} = value, opts \\ []) do
modify(bin, @insert, [index, value | flags_policy(opts)])
end
@doc "Inserts list expression `values` at `index`. Supports `policy: [flags: flags]`."
@spec insert_items(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def insert_items(%Exp{} = bin, %Exp{} = index, %Exp{} = values, opts \\ []) do
modify(bin, @insert_items, [index, values | flags_policy(opts)])
end
@doc "Sets the value at `index`. Supports `policy: [flags: flags]`."
@spec set(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def set(%Exp{} = bin, %Exp{} = index, %Exp{} = value, opts \\ []) do
modify(bin, @set, [index, value | flags_policy(opts)])
end
@doc "Removes all values from the list expression."
@spec clear(Exp.t()) :: t()
def clear(%Exp{} = bin), do: modify(bin, @clear, [])
@doc "Increments the numeric value at `index`. Supports `policy:`."
@spec increment(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def increment(%Exp{} = bin, %Exp{} = index, %Exp{} = value, opts \\ []) do
modify(bin, @increment, [index, value | list_policy(opts)])
end
@doc "Sorts the list expression using `sort_flags`."
@spec sort(Exp.t(), ListOp.sort_flags() | keyword()) :: t()
def sort(%Exp{} = bin, sort_flags_or_opts \\ :default) do
sort_flags =
case sort_flags_or_opts do
opts when is_list(opts) ->
if Keyword.keyword?(opts), do: Keyword.get(opts, :sort_flags, :default), else: opts
sort_flags ->
sort_flags
end
modify(bin, @sort, [PolicyInteger.list_sort_flags(sort_flags)])
end
@doc "Removes values equal to `value`, returning data selected by `return_type:`."
@spec remove_by_value(Exp.t(), Exp.t(), opts()) :: t()
def remove_by_value(%Exp{} = bin, %Exp{} = value, opts \\ []) do
modify(bin, @remove_by_value, [rt(opts), value])
end
@doc "Removes values matching any expression in `values`, returning selected data."
@spec remove_by_value_list(Exp.t(), Exp.t(), opts()) :: t()
def remove_by_value_list(%Exp{} = bin, %Exp{} = values, opts \\ []) do
modify(bin, @remove_by_value_list, [rt(opts), values])
end
@doc "Removes values in `[begin_value, end_value)`, returning selected data."
@spec remove_by_value_range(Exp.t(), Exp.t() | nil, Exp.t() | nil, opts()) :: t()
def remove_by_value_range(%Exp{} = bin, begin_value, end_value, opts \\ []) do
modify(bin, @remove_by_value_interval, range_args(rt(opts), begin_value, end_value))
end
@doc "Removes values nearest to `value` and greater by relative `rank`."
@spec remove_by_value_rel_rank_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def remove_by_value_rel_rank_range(%Exp{} = bin, %Exp{} = value, %Exp{} = rank, opts \\ []) do
modify(bin, @remove_by_value_rel_rank_range, [rt(opts), value, rank])
end
@doc "Removes `count` values nearest to `value` and greater by relative `rank`."
@spec remove_by_value_rel_rank_range_count(Exp.t(), Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def remove_by_value_rel_rank_range_count(
%Exp{} = bin,
%Exp{} = value,
%Exp{} = rank,
%Exp{} = count,
opts \\ []
) do
modify(bin, @remove_by_value_rel_rank_range, [rt(opts), value, rank, count])
end
@doc "Removes the value at `index`."
@spec remove_by_index(Exp.t(), Exp.t()) :: t()
def remove_by_index(%Exp{} = bin, %Exp{} = index) do
modify(bin, @remove_by_index, [return_none(), index])
end
@doc "Removes values from `index` through the end, returning selected data."
@spec remove_by_index_range_from(Exp.t(), Exp.t(), opts()) :: t()
def remove_by_index_range_from(%Exp{} = bin, %Exp{} = index, opts \\ []) do
modify(bin, @remove_by_index_range, [rt(opts), index])
end
@doc "Removes `count` values from `index`, returning selected data."
@spec remove_by_index_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def remove_by_index_range(%Exp{} = bin, %Exp{} = index, %Exp{} = count, opts \\ []) do
modify(bin, @remove_by_index_range, [rt(opts), index, count])
end
@doc "Removes the value at `rank`."
@spec remove_by_rank(Exp.t(), Exp.t()) :: t()
def remove_by_rank(%Exp{} = bin, %Exp{} = rank) do
modify(bin, @remove_by_rank, [return_none(), rank])
end
@doc "Removes values from `rank` through the highest rank, returning selected data."
@spec remove_by_rank_range_from(Exp.t(), Exp.t(), opts()) :: t()
def remove_by_rank_range_from(%Exp{} = bin, %Exp{} = rank, opts \\ []) do
modify(bin, @remove_by_rank_range, [rt(opts), rank])
end
@doc "Removes `count` values from `rank`, returning selected data."
@spec remove_by_rank_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def remove_by_rank_range(%Exp{} = bin, %Exp{} = rank, %Exp{} = count, opts \\ []) do
modify(bin, @remove_by_rank_range, [rt(opts), rank, count])
end
@doc "Returns the number of values in the list expression."
@spec size(Exp.t()) :: t()
def size(%Exp{} = bin), do: read(bin, :int, @size, [])
@doc "Returns values equal to `value`, selected by `return_type:`."
@spec get_by_value(Exp.t(), Exp.t(), opts()) :: t()
def get_by_value(%Exp{} = bin, %Exp{} = value, opts \\ []) do
read(bin, list_return_type(opts), @get_by_value, [rt(opts), value])
end
@doc "Returns values matching any expression in `values`, selected by `return_type:`."
@spec get_by_value_list(Exp.t(), Exp.t(), opts()) :: t()
def get_by_value_list(%Exp{} = bin, %Exp{} = values, opts \\ []) do
read(bin, list_return_type(opts), @get_by_value_list, [rt(opts), values])
end
@doc "Returns values in `[begin_value, end_value)`, selected by `return_type:`."
@spec get_by_value_range(Exp.t(), Exp.t() | nil, Exp.t() | nil, opts()) :: t()
def get_by_value_range(%Exp{} = bin, begin_value, end_value, opts \\ []) do
read(
bin,
list_return_type(opts),
@get_by_value_interval,
range_args(rt(opts), begin_value, end_value)
)
end
@doc "Returns values nearest to `value` and greater by relative `rank`."
@spec get_by_value_rel_rank_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def get_by_value_rel_rank_range(%Exp{} = bin, %Exp{} = value, %Exp{} = rank, opts \\ []) do
read(bin, list_return_type(opts), @get_by_value_rel_rank_range, [rt(opts), value, rank])
end
@doc "Returns `count` values nearest to `value` and greater by relative `rank`."
@spec get_by_value_rel_rank_range_count(Exp.t(), Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def get_by_value_rel_rank_range_count(
%Exp{} = bin,
%Exp{} = value,
%Exp{} = rank,
%Exp{} = count,
opts \\ []
) do
read(bin, list_return_type(opts), @get_by_value_rel_rank_range, [rt(opts), value, rank, count])
end
@doc "Returns the value at `index`, selected by `return_type:`."
@spec get_by_index(Exp.t(), Exp.t(), Exp.exp_type(), opts()) :: t()
def get_by_index(%Exp{} = bin, %Exp{} = index, value_type, opts \\ []) do
read(bin, single_return_type(opts, value_type), @get_by_index, [rt(opts), index])
end
@doc "Returns values from `index` through the end, selected by `return_type:`."
@spec get_by_index_range_from(Exp.t(), Exp.t(), opts()) :: t()
def get_by_index_range_from(%Exp{} = bin, %Exp{} = index, opts \\ []) do
read(bin, list_return_type(opts), @get_by_index_range, [rt(opts), index])
end
@doc "Returns `count` values from `index`, selected by `return_type:`."
@spec get_by_index_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def get_by_index_range(%Exp{} = bin, %Exp{} = index, %Exp{} = count, opts \\ []) do
read(bin, list_return_type(opts), @get_by_index_range, [rt(opts), index, count])
end
@doc "Returns the value at `rank`, selected by `return_type:`."
@spec get_by_rank(Exp.t(), Exp.t(), Exp.exp_type(), opts()) :: t()
def get_by_rank(%Exp{} = bin, %Exp{} = rank, value_type, opts \\ []) do
read(bin, single_return_type(opts, value_type), @get_by_rank, [rt(opts), rank])
end
@doc "Returns values from `rank` through the highest rank, selected by `return_type:`."
@spec get_by_rank_range_from(Exp.t(), Exp.t(), opts()) :: t()
def get_by_rank_range_from(%Exp{} = bin, %Exp{} = rank, opts \\ []) do
read(bin, list_return_type(opts), @get_by_rank_range, [rt(opts), rank])
end
@doc "Returns `count` values from `rank`, selected by `return_type:`."
@spec get_by_rank_range(Exp.t(), Exp.t(), Exp.t(), opts()) :: t()
def get_by_rank_range(%Exp{} = bin, %Exp{} = rank, %Exp{} = count, opts \\ []) do
read(bin, list_return_type(opts), @get_by_rank_range, [rt(opts), rank, count])
end
defp read(bin, type, op_code, args), do: Module.cdt_read(bin, type, op_code, args)
defp modify(bin, op_code, args), do: Module.cdt_modify(bin, :list, op_code, args, :list)
defp rt(opts),
do: opts |> Keyword.get(:return_type, return_value()) |> PolicyInteger.list_return_type()
defp list_return_type(opts), do: list_return_type_from_rt(rt(opts))
defp single_return_type(opts, value_type), do: single_return_type_from_rt(rt(opts), value_type)
defp list_return_type_from_rt(return_type) do
case return_type &&& bnot(@inverted) do
value when value in [1, 2, 3, 4, 7] -> :list
5 -> :int
13 -> :bool
end
end
defp single_return_type_from_rt(return_type, value_type) do
case return_type &&& bnot(@inverted) do
value when value in [1, 2, 3, 4, 5] -> :int
7 -> value_type
13 -> :bool
end
end
defp list_policy(opts) do
case Keyword.get(opts, :policy) do
nil ->
[]
policy ->
%{order: order, flags: flags} = PolicyInteger.list_policy(policy)
[order, flags]
end
end
defp flags_policy(opts) do
case Keyword.get(opts, :policy) do
nil -> []
policy -> [PolicyInteger.list_policy(policy).flags]
end
end
defp range_args(return_type, begin_value, nil), do: [return_type, begin_value]
defp range_args(return_type, begin_value, end_value), do: [return_type, begin_value, end_value]
end