`ASOF`
Generic Description
ASOF is a query-level prefix that runs a normal Cypher query with a point-in-time read context for declared temporal properties and stream-backed abstraction features.
ASOF 1743465600000
MATCH (m:Member)
RETURN m.status, m.plan
The timestamp can be an epoch-millisecond integer or a literal datetime constructor:
ASOF datetime('2026-04-01T00:00:00Z')
MATCH (m:Member)
RETURN m.status, m.plan
Consumer-Level Explanation
Use ASOF when you want to ask: “what would this normal query have seen at this time?”
Without ASOF, property reads behave exactly as they do today. A query such as:
MATCH (m:Member)
RETURN m.status, m.plan
reads the current graph and the current materialized property values.
With ASOF, the shape of the query stays the same, but declared temporal properties are read from their hidden history as of the supplied timestamp. Non-temporal properties, labels, relationships, and normal graph structure continue to behave as current graph data.
That means m.status becomes time-aware only if status is declared as a temporal property for one of m's labels. If m.plan is an ordinary property, m.plan stays the normal current value.
Materialized abstraction features use the same latest-as-of behavior:
ASOF 1743465600000
RETURN actor(Member:'member-102').churn_risk AS churn_risk
The selector reads the materialized feature stream for the Member abstraction subject and returns the latest churn_risk value at or before the ASOF timestamp. Without ASOF, it returns the latest materialized stream value available now.
Conceptual Explanation
ASOF is not a separate feature-query language and it is not a full database snapshot restore.
It is a read context carried through the normal Cypher planning and execution path. The planner attaches a temporal-property resolver only when the logical plan contains an ASOF timestamp. The resolver is consulted by property access in filters and projections:
m.statusWHERE m.status = 'active'RETURN m- expressions that evaluate a property through the normal expression evaluator
For declared temporal properties, the resolver reads the latest temporal-property history row at or before the ASOF timestamp. If the property is declared temporal but has no value yet at that time, it resolves as null. If the property is not declared temporal for the node labels, the normal current property value is used.
For object-valued temporal properties, this is still a normal property read. m.profile_state under ASOF returns the latest object value at or before the timestamp, not a timestamped history row. Use nexyron.temporal_history when the query needs ts, property, and value rows.
ASOF also caps temporal-property procedure reads in the same query. If nexyron.temporal_history or nexyron.temporal_rollup receives null as its upper bound, the effective upper bound is the ASOF timestamp. If it receives an explicit upper bound later than the ASOF timestamp, the effective upper bound is the smaller ASOF timestamp.
The same cap applies to nexyron.feature_history. A feature-history call under ASOF will not return stream points after the query-level timestamp, even when to is omitted or later than the ASOF timestamp.
This design keeps existing feature contracts and user queries clean. A stored feature query can stay as ordinary Cypher, and snapshot/backfill execution can run that same query under an ASOF context when the caller supplies an at timestamp.
Advanced Example
ASOF datetime('2026-04-01T00:00:00Z')
MATCH (m:Member)-[:BELONGS_TO]->(plan:Plan)
WHERE m.status = 'active'
RETURN plan.name AS plan_name,
count(m) AS active_members,
avg(m.risk_score) AS avg_risk_score
ORDER BY active_members DESC
In this query:
m.statusis resolved from temporal history if:Member.statusis declared temporal.m.risk_scoreis resolved from temporal history if:Member.risk_scoreis declared temporal.plan.nameremains the current plan name unless it is also declared as a temporal property on:Plan.- The
MATCHstructure still uses the current graph topology.
For abstraction features, subject ids can be literals, parameters, or query variables:
ASOF 1743465600000
WITH 'member-102' AS member_id
RETURN actor(Member:<member_id>).churn_risk AS churn_risk
<member_id> is selector sugar for the visible Cypher variable member_id. The feature name is resolved against the registered feature owned by the Member abstraction, usually a fully qualified registry name such as actor.churn_risk or member.churn_risk.
Real Use Cases
- Backfilling semantic snapshot rows without changing every stored feature query to include timestamp filters.
- Replaying membership, account, device, or risk-state features as they existed before a training cutoff.
- Auditing a decision by running the same ordinary query under the historical property state that should have been visible at decision time.
- Reading a materialized abstraction feature with
actor(Member:'id').paramso the query surface behaves like temporal node-property access. - Comparing current graph structure with historical temporal-property values when only selected properties are modeled as changing streams.
Real Limitations And Tradeoffs
ASOFcurrently applies to declared temporal properties, not to labels, relationships, or arbitrary historical graph topology.- It is read-only. Mutating statements under
ASOFare rejected because the prefix describes a historical read context, not a write mode. - The timestamp must be known when the query is translated: use an epoch-millisecond integer or a literal
datetime('...')expression. - If a temporal property did not have any history row at or before the requested time, it resolves to
null; Nexyron does not silently fall back to the current value for declared temporal properties. - Abstraction feature selectors read materialized feature streams. If no snapshot materialization has written the feature stream point for the subject at or before the effective time, the selector resolves to
null; it does not compute the feature query on demand. - The selector form is
actor(Member:'id').param,stage(Store:$id).param,society(Region:<region_id>).param, orenvironment(Context:'id').param. The part after the dot is a feature selector that must resolve to exactly one registered feature on that abstraction. - For windowed history analytics such as counts, averages, rates, rolling windows, or raw event timelines, use
nexyron.temporal_history,nexyron.temporal_rollup,TIME,WINDOW, or temporal sequence functions. When those temporal-property procedures run underASOF, their upper time bound is capped to theASOFtimestamp.