Skip to content

Locy Syntax Cheatsheet

Rule

CREATE RULE name [PRIORITY n] AS
MATCH ...
[WHERE ...]                         // pre-aggregation filter
[ALONG x = expr]
[FOLD agg = aggregate(expr)]
[WHERE agg_condition]               // post-FOLD filter (HAVING)
[BEST BY expr ASC|DESC]
YIELD KEY a, value AS alias, prob_expr AS PROB
// OR, for graph mutation rules (edge/node props are inline maps, no SET):
DERIVE (src)-[:TYPE {prop: expr}]->(dst)

The second WHERE (after FOLD) filters on aggregated values — equivalent to SQL's HAVING. It can reference FOLD output columns and KEY columns.

FOLD Aggregators

Operator Semantics Use In Recursion?
COUNT(*) / COUNT(expr) Row count Safe (unbounded)
SUM(expr) Arithmetic sum Non-recursive only
AVG(expr) Arithmetic mean Non-recursive only
MIN(expr) Minimum value Safe in recursion
MAX(expr) Maximum value Safe in recursion
COLLECT(expr) Collect into list Safe (unbounded)
MSUM(expr) Monotonic sum (non-decreasing) Safe (unbounded)
MMAX(expr) Monotonic maximum Safe in recursion
MMIN(expr) Monotonic minimum Safe in recursion
MCOUNT(expr) Monotonic count Safe (unbounded)
MNOR(expr) Noisy-OR probability: 1 − ∏(1 − pᵢ) Safe in recursion
MPROD(expr) Product probability: ∏ pᵢ Safe in recursion

Recursion safety comes from the aggregate's registered semilattice (monotone_join), not from an M prefix; the compiler falls back to the six built-in M* names only when the aggregate registry has no entry. SUM and AVG are non-monotone and are rejected inside a recursive stratum.

The aggregates marked unbounded are monotone but have no top element, so a recursive fold over one can iterate until max_iterations rather than converging — the iteration cap is the backstop. The exception is COLLECT, which is assembled after the fixpoint rather than tracked inside it, so it does not itself keep the loop iterating.

Goal Query

QUERY name [WHERE ...] [RETURN ...]

Derive Command

DERIVE name [WHERE ...]

Explain

EXPLAIN RULE name [WHERE ...]

Assume

ASSUME { <cypher mutations> } THEN { <locy/cypher body> }

Abduce

ABDUCE [NOT] name [WHERE ...] [RETURN ...]

Neural Predicates

CREATE MODEL name AS
  INPUT (binding [: Label])
  [FEATURES feature_expr (, feature_expr)*]
  [FEATURES (subject, column) FROM source_rule]
  OUTPUT (PROB | SCORE | LABEL | VECTOR) result_name
  USING xervo('provider_alias' [, embedder = 'embed_alias'])
  [CALIBRATION (platt_scaling | isotonic_regression | temperature_scaling | beta_calibration | dirichlet | conformal | conformal(alpha) | none)]
  [VERSION 'string']

CALIBRATE model_name ON MATCH pattern [WHERE expr] TARGET expr METHOD calibration_method [HOLDOUT n]
VALIDATE  model_name ON MATCH pattern [WHERE expr] TARGET expr METRICS metric (, metric)*

metricbrier_score | ece | debiased_ece | accuracy | log_loss | auc. The classifier-registry key is the CREATE MODEL <name>, not the USING xervo('alias') provider hint. The feature dict the callable receives is keyed by the INPUT binding name; values are the evaluated argument expressions at the call site. See Neural Predicates.

Modules

MODULE my.module
USE shared.rules