Query structure for AGE graph queries.
Summary
Functions
Adds a parameter to the query, returning the updated query and a $paramN reference.
Builds the Cypher for a single aggregate over the query's filtered set.
Builds the Cypher for a bulk destroy by query.
Pre-write duplicate-PK check: groups the WHERE-matched set by primary key and
returns one row iff any PK matches 2+ vertices. AGE enforces no PK uniqueness,
so a bulk SET could write multiple physical rows for one input PK — this is run
BEFORE the SET so the anomaly fails closed without writing (works under any
transaction mode, and keys on the SOURCE PK so a SET that rewrites the PK can't
evade it). Verified AGE 1.6.0 form (aliased grouping keys + WITH...WHERE).
Returns {cypher, params}; a non-empty result means a duplicate exists.
Converts a query to Cypher with parameters.
Builds the bulk-update Cypher for update_query/4: applies one set of changes
(plain attributes + translated atomics) to every record the query matches.
Types
@type t() :: %AshAge.Query{ aggregates: [Ash.Query.Aggregate.t()], expression: Ash.Filter.t() | nil, filters: [String.t()], graph: atom() | String.t(), label: atom() | String.t(), limit: non_neg_integer() | nil, offset: non_neg_integer() | nil, params: map(), repo: module(), resource: module(), sort: [{atom(), :asc | :desc}], tenant: term() | nil }
Functions
Adds a parameter to the query, returning the updated query and a $paramN reference.
Builds the Cypher for a single aggregate over the query's filtered set.
MATCH (n:LABEL) WHERE <main filter> [AND <aggregate sub-filter>] RETURN <expr> AS agg.
Deliberately emits NO LIMIT/SKIP/ORDER BY even when the query carries them
(the AshPostgres/ETS contract): an aggregate is computed over the FULL filtered
set, not a page of it. The aggregate's own sub-query filter (its query field)
is AND-ed into the WHERE so each aggregate narrows independently (one query is
issued per aggregate by run_aggregate_query/3).
Builds the Cypher for a bulk destroy by query.
MATCH (n:LABEL) WHERE <translated filter> [WITH n SKIP .. LIMIT ..] DETACH DELETE n.
Uses the SAME build_where as the read path, so destroy_query deletes exactly the
rows a read would return — including the tenant predicate Ash attaches for
:attribute multitenancy. LIMIT/SKIP are honored via a WITH n pass-through:
Ash does NOT slice the query above the data layer for destroy_query
(bulk.ex:622-723 do_atomic_destroy passes it through), so a user-supplied limit
must bound the deletion here or it over-deletes (the ETS destroy_query reference
honors limit too). No ORDER BY: without a deterministic sort, which rows die is
unspecified — same as ETS, which sorts by PK only.
Pre-write duplicate-PK check: groups the WHERE-matched set by primary key and
returns one row iff any PK matches 2+ vertices. AGE enforces no PK uniqueness,
so a bulk SET could write multiple physical rows for one input PK — this is run
BEFORE the SET so the anomaly fails closed without writing (works under any
transaction mode, and keys on the SOURCE PK so a SET that rewrites the PK can't
evade it). Verified AGE 1.6.0 form (aliased grouping keys + WITH...WHERE).
Returns {cypher, params}; a non-empty result means a duplicate exists.
Converts a query to Cypher with parameters.
Returns {cypher_string, params_map}.
Builds the bulk-update Cypher for update_query/4: applies one set of changes
(plain attributes + translated atomics) to every record the query matches.
Mirrors delete_cypher/2, replacing DETACH DELETE n with SET <clauses> RETURN n.
Scoping source is build_where(query) — the :attribute tenant predicate arrives
in query.expression via Ash's handle_attribute_multitenancy (NOT changeset.filter,
which Ash nils into query.filter before the data layer runs — adversarial Challenge 1).
LIMIT/SKIP honored via the same WITH n pass-through as destroy_query.
RETURN n so updated records can be decoded when return_records? is set.