Arcadic.Vector (Arcadic v0.2.0)

Copy Markdown View Source

ArcadeDB dense vector search — tenant-blind index DDL and nearest-neighbour / hybrid-fusion query builders over ArcadeDB's LSM_VECTOR surface.

Every caller value (query vector, k, ef_search, max_distance) binds as a $param. The only text interpolated into a statement is the index reference "Type[property]", whose two identifiers are Arcadic.Identifier-validated before composition — the sole injection surface, closed by construction (callers pass type/property, never a raw ref). Index metadata values (dimensions, the similarity/encoding/quantization enums) are developer-supplied schema config, validated against integer/allowlist checks before interpolation. Failures carry the invalid SHAPE only, never the offending value (AGENTS.md Critical Rule 3).

Summary

Functions

Creates a dense LSM_VECTOR index (idempotent — IF NOT EXISTS). opts: similarity (:cosine default | :dot_product | :euclidean), encoding (:float32 | :int8), quantization (:none | :int8 | :binary | :product), max_connections (default 16), beam_width (default 100). Unknown opt keys are rejected value-free (the server silently accepts unknown METADATA keys).

Creates a dense vector index, raising on error.

Drops a dense vector index (idempotent — IF EXISTS).

Drops a dense vector index, raising on error.

Runs a hybrid fusion over dense neighbour subqueries. neighbor_specs is a non-empty list of {type, property, query_vector, k}, each built as a validated vector.neighbors subquery with distinct indexed params. opts: fusion (:rrf default | :dbsf | :linear), weights (list of numbers), k (pos_integer).

Runs a hybrid fusion, returning rows or raising.

Builds the ArcadeDB index reference "Type[property]", validating both identifiers.

Runs a dense nearest-neighbour search, returning rows ranked closest-first (each carries the vertex's top-level fields plus distance). query_vector and k bind as params. opts: ef_search (pos_integer), max_distance (number) — both bind as params inside the query-options object.

Runs a dense nearest-neighbour search, returning rows or raising.

Functions

create_dense_index(conn, type, property, dimensions, opts \\ [])

@spec create_dense_index(
  Arcadic.Conn.t(),
  String.t(),
  String.t(),
  pos_integer(),
  keyword()
) ::
  :ok | {:error, atom() | Exception.t()}

Creates a dense LSM_VECTOR index (idempotent — IF NOT EXISTS). opts: similarity (:cosine default | :dot_product | :euclidean), encoding (:float32 | :int8), quantization (:none | :int8 | :binary | :product), max_connections (default 16), beam_width (default 100). Unknown opt keys are rejected value-free (the server silently accepts unknown METADATA keys).

create_dense_index!(conn, type, property, dimensions, opts \\ [])

@spec create_dense_index!(
  Arcadic.Conn.t(),
  String.t(),
  String.t(),
  pos_integer(),
  keyword()
) :: :ok

Creates a dense vector index, raising on error.

drop_dense_index(conn, type, property)

@spec drop_dense_index(Arcadic.Conn.t(), String.t(), String.t()) ::
  :ok | {:error, atom() | Exception.t()}

Drops a dense vector index (idempotent — IF EXISTS).

drop_dense_index!(conn, type, property)

@spec drop_dense_index!(Arcadic.Conn.t(), String.t(), String.t()) :: :ok

Drops a dense vector index, raising on error.

fuse(conn, neighbor_specs, opts \\ [])

@spec fuse(
  Arcadic.Conn.t(),
  [{String.t(), String.t(), [number()], pos_integer()}],
  keyword()
) ::
  {:ok, [map()]} | {:error, atom() | Exception.t()}

Runs a hybrid fusion over dense neighbour subqueries. neighbor_specs is a non-empty list of {type, property, query_vector, k}, each built as a validated vector.neighbors subquery with distinct indexed params. opts: fusion (:rrf default | :dbsf | :linear), weights (list of numbers), k (pos_integer).

Fused rows are ranked by ArcadeDB's fusion score (higher = better) rather than the distance that neighbors/6 returns.

neighbor_specs and weights are trusted developer-supplied config: the emitted statement grows linearly with their length (no cap), matching arcadic's no-statement-size-limit posture elsewhere.

fuse!(conn, neighbor_specs, opts \\ [])

@spec fuse!(
  Arcadic.Conn.t(),
  [{String.t(), String.t(), [number()], pos_integer()}],
  keyword()
) :: [
  map()
]

Runs a hybrid fusion, returning rows or raising.

index_ref(type, property)

@spec index_ref(String.t(), String.t()) ::
  {:ok, String.t()} | {:error, :invalid_identifier}

Builds the ArcadeDB index reference "Type[property]", validating both identifiers.

neighbors(conn, type, property, query_vector, k, opts \\ [])

@spec neighbors(
  Arcadic.Conn.t(),
  String.t(),
  String.t(),
  [number()],
  pos_integer(),
  keyword()
) ::
  {:ok, [map()]} | {:error, atom() | Exception.t()}

Runs a dense nearest-neighbour search, returning rows ranked closest-first (each carries the vertex's top-level fields plus distance). query_vector and k bind as params. opts: ef_search (pos_integer), max_distance (number) — both bind as params inside the query-options object.

distance (and therefore max_distance) semantics depend on the index's similarity: COSINE yields 0..1 ascending (0 = identical); DOT_PRODUCT yields NEGATIVE values (≈ -1 identical, less-negative = farther), so a small positive max_distance filters nothing; EUCLIDEAN differs again. Choose thresholds per the index's similarity.

neighbors!(conn, type, property, query_vector, k, opts \\ [])

@spec neighbors!(
  Arcadic.Conn.t(),
  String.t(),
  String.t(),
  [number()],
  pos_integer(),
  keyword()
) :: [
  map()
]

Runs a dense nearest-neighbour search, returning rows or raising.