AshArcadic.Query (AshArcadic v0.1.0)

Copy Markdown View Source

Query structure for AshArcadic. Accumulates filters/sort/limit/offset; compiled to Cypher by to_cypher/1 (Plan 2). client/database/label come from the resource's arcade DSL; database is overridden per-tenant by set_tenant/3 for :context resources (Plan 2).

Summary

Types

t()

A stashed vector search (set by AshArcadic.Preparations.VectorSearch, consumed by run_query/2). kind selects the arcadic surface; the run path's per-kind malformed guard enforces the exact shape. :dense carries index/query_vector; :sparse carries the resolved tokens_property/weights_property + query_tokens/query_weights; :hybrid carries arms (a list of per-arm maps). opts carries passthrough options (dense: ef_search/max_distance; sparse: group_by/group_size; hybrid: fusion/weights/k/group_by/group_size).

Functions

Adds a parameter to the query, returning the updated query and a $paramN reference. Skips any paramN key already present so a seeded key (SET-attribute or match_<pk> on the update/destroy path) is never clobbered.

Compiles the candidate-RID Cypher for the vector-search Phase 1 — MATCH (n:<label>) [WHERE <filters [AND extra]>] RETURN n. Reuses the read path's build_where/1 (caller/policy filters) and appends the optional self-injected tenant predicate extra ({clause, params} from the data layer). RETURN n yields flat rows carrying @rid; the data layer reads @rid off each and passes them to Arcadic.Vector.neighbors(filter: rids). No ORDER/SKIP/LIMIT — the whole scoped set is the candidate set (vector reads reject paging).

Compiles the query to {cypher, params}. Emits MATCH (n:<label>) [WHERE …] RETURN n [ORDER BY …] [SKIP n] [LIMIT n]. label and every sort field are re-validated as identifiers here (defense-in-depth — they feed the statement body; values ride params).

Types

t()

@type t() :: %AshArcadic.Query{
  aggregates: [Ash.Query.Aggregate.t()],
  calculations: [{Ash.Query.Calculation.t(), Ash.Expr.t()}],
  client: module() | nil,
  combination_of: [{atom(), t()}],
  database: String.t() | nil,
  distinct: [{atom(), atom()}],
  distinct_sort: [{atom(), atom()}],
  expression: Ash.Filter.t() | nil,
  filters: [String.t()],
  internal?: boolean(),
  label: atom() | String.t() | nil,
  limit: non_neg_integer() | nil,
  offset: non_neg_integer() | nil,
  params: map(),
  resource: module(),
  sort: [{atom(), :asc | :desc} | {:expr, String.t(), atom()}],
  tenant: term() | nil,
  vector_search: nil | vector_search()
}

vector_search()

@type vector_search() ::
  %{
    kind: :dense,
    index: atom(),
    query_vector: [number()],
    k: pos_integer(),
    allow_global?: boolean(),
    opts: keyword()
  }
  | %{
      kind: :sparse,
      index: atom(),
      tokens_property: atom(),
      weights_property: atom(),
      query_tokens: [integer()],
      query_weights: [number()],
      k: pos_integer(),
      allow_global?: boolean(),
      opts: keyword()
    }
  | %{kind: :hybrid, arms: [map()], allow_global?: boolean(), opts: keyword()}

A stashed vector search (set by AshArcadic.Preparations.VectorSearch, consumed by run_query/2). kind selects the arcadic surface; the run path's per-kind malformed guard enforces the exact shape. :dense carries index/query_vector; :sparse carries the resolved tokens_property/weights_property + query_tokens/query_weights; :hybrid carries arms (a list of per-arm maps). opts carries passthrough options (dense: ef_search/max_distance; sparse: group_by/group_size; hybrid: fusion/weights/k/group_by/group_size).

Functions

add_param(query, value)

@spec add_param(t(), term()) :: {t(), String.t()}

Adds a parameter to the query, returning the updated query and a $paramN reference. Skips any paramN key already present so a seeded key (SET-attribute or match_<pk> on the update/destroy path) is never clobbered.

candidate_rid_cypher(query, extra)

@spec candidate_rid_cypher(t(), nil | {String.t(), map()}) :: {String.t(), map()}

Compiles the candidate-RID Cypher for the vector-search Phase 1 — MATCH (n:<label>) [WHERE <filters [AND extra]>] RETURN n. Reuses the read path's build_where/1 (caller/policy filters) and appends the optional self-injected tenant predicate extra ({clause, params} from the data layer). RETURN n yields flat rows carrying @rid; the data layer reads @rid off each and passes them to Arcadic.Vector.neighbors(filter: rids). No ORDER/SKIP/LIMIT — the whole scoped set is the candidate set (vector reads reject paging).

to_cypher(query)

@spec to_cypher(t()) :: {String.t(), map()}

Compiles the query to {cypher, params}. Emits MATCH (n:<label>) [WHERE …] RETURN n [ORDER BY …] [SKIP n] [LIMIT n]. label and every sort field are re-validated as identifiers here (defense-in-depth — they feed the statement body; values ride params).