Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

openCypher Support Matrix

A tracked feature matrix for Fluree’s openCypher surface, against openCypher 9 (the Cypher 9 language reference the openCypher TCK exercises). For syntax and semantics see Cypher (concept); for recipes see the Cypher cookbook.

Legend

MarkMeaning
Supported — works as openCypher 9 specifies.
Partial — a common subset works; specific forms are deferred (noted).
Divergent by design — intentionally different because Fluree is multi-modal graph. Rejected-or-adapted, never silently wrong.
Deferred — not yet implemented; rejected with a clear error.

Guiding invariant: an unsupported construct produces a clear error, never a silently wrong result. Divergences (⟂) are where openCypher’s LPG assumptions meet Fluree’s RDF model.

Core model divergences (⟂)

These shape everything below; read them first.

  • Nodes are IRIs. A node is an RDF subject (an IRI/blank node), not an opaque LPG node. labels(n) are rdf:type assertions; node identity is the IRI.
  • Relationships are edge annotations. A relationship is the base triple (s, p, o); binding -[r:T]-> reifies it into an f:reifies* annotation node (the LPG edge identity). Fluree does not implement RDF-star triple terms — see Edge annotations.
  • id(n) / elementId(n) return the node/relationship IRI string — there is no integer element id.
  • No implicit per-statement transaction id semantics; immutability/time-travel replace it (f:t, history queries).

Clauses

ClauseStatusNotes
MATCHNode/relationship patterns, WHERE.
OPTIONAL MATCHNullable bindings; poisoned-binding semantics.
WITHProjection boundary; WHERE→HAVING when it references aggregates; DISTINCT/ORDER BY/SKIP/LIMIT; collect() carries forward as a list. Before a write clause only the pass-through / rename / computed-alias / filter subset is allowed (no aggregation / DISTINCT / ORDER BY / SKIP / LIMIT); WITH before DELETE deferred.
UNWINDInline lists and runtime list expressions; $param lists via API substitution.
RETURN*, aliases, DISTINCT, ORDER BY/SKIP/LIMIT. SKIP/LIMIT must be literal integers.
UNION / UNION ALLColumn-name-match + uniform-variant rules enforced.
CALL { … } (subquery)Imports (a,b) / (*), uncorrelated broadcast, inner UNION, nesting, strict scope/shadowing, correlated-aggregate soundness.
CREATENodes + relationships (relationships reify).
MERGE (node)Find-or-create with ON CREATE SET. ON MATCH SET deferred (for all MERGE forms — node and relationship).
MERGE (relationship)Standalone + bound-endpoint forms; ON CREATE SET. Deferred: property-bearing rel MERGE (-[:T {p:v}]->), multi-hop / multi-part MERGE, multiple MERGE clauses, and MERGE combined with another write clause in one statement.
SET / REMOVEProperties, += map merge, labels.
DELETE / DETACH DELETE
FOREACH
CALL proc(...) YIELDStored/builtin procedures (distinct from CALL { … }).
LOAD CSVBulk CSV import exists via the CLI, not the LOAD CSV clause.
Multi-statement (;)One statement per request.

Patterns & paths

FeatureStatusNotes
Node pattern (labels, inline props)
Bare unconstrained MATCH (n)Rejected — a node must be constrained by a label, property, or relationship (no whole-graph scan).
Directed typed relationship -[:T]->, <-[:T]-
Type alternation -[:A|B]->Union of concrete predicates.
Undirected -[:T]-Forward ∪ reverse Union.
Untyped relationship -[r]->Variable predicate; system facts hidden.
Bounded var-length -[:T*m..n]->Enumerates trails (one row per path, relationship-uniqueness).
Unbounded var-length -[:T*]->Reachability (one row per reachable endpoint), not path enumeration.
Untyped var-length -[*m..n]->Wildcard reachability over node→node edges; excludes rdf:type/f:reifies*. A direction is required (-[*]- deferred); an unbounded lower bound above 1 (-[*2..]->) deferred — give an upper bound or name a type.
Bounded var-length binding -[r:T*m..n]-> / p = …r = rel list, p = path; via per-branch construction.
Unbounded var-length bindingNeeds a path-enumeration operator.
shortestPath / allShortestPathsAnchored, single typed predicate; All emits one row per minimal path.
relationships(p) / nodes(p) / pathPairs(p) / length(p)relationships(p) carries the stored edge orientation.
Bounded type-alternation var-length -[:A|B*1..3]->Use the unbounded form.
Undirected unbounded path -[:T*]-
Free path value MATCH p = (...) without a shortestPath/allShortestPaths wrapperWrap with a path-finding function.
Zero-length typed bounded path -[:T*0..M]->Use *1..M.
Property filter on a var-length / shortestPath relationship

Expressions & operators

FeatureStatusNotes
Arithmetic + - * / %, unary -/ of integers → xsd:decimal (rendered as a string for precision).
Exponentiation ^Right-associative.
Comparison = <> < <= > >=
Boolean AND / OR / XOR / NOT
STARTS WITH / ENDS WITH / CONTAINS
x IN [ … ]
IS NULL / IS NOT NULL
CASE (simple + generic)Aggregates inside CASE deferred; CASE/EXISTS inside a write-statement MATCH … WHERE deferred.
NULL literalUse an absent value / IS NULL.
Property access n.propBare-variable target; chained n.a.b deferred — except temporal-field chains like x.date.month, which lower to an extraction function.
List literal / indexing [a,b], list[i]Negative index from end.
Map literal {k: v}Key-order-insensitive identity (⟂ vs strict insertion order for equality).
Map projection n{.k, .*, x: e}Mixing .* with other selectors deferred.
List comprehension / reduce / all·any·none·singleLoop-local property access supported.
Pattern comprehension [(a)-->(b) | e]Correlated; reuses the EXISTS path.
EXISTS { … } (predicate + value)Incl. inside map/projection entries.
Parameters $pScalars, lists, maps; substituted everywhere incl. inside CALL/patterns.

Functions

GroupStatusNotes
Casts: toString toInteger toFloattoFloat→xsd:double.
String: toUpper toLower substring left right trim ltrim rtrim replace split reversesubstring 0-indexed; replace literal.
Math: abs round floor ceil sign sqrt log randlog = natural log.
coalesce
Aggregates: count sum avg min max collect (+ DISTINCT)Implicit grouping by non-aggregate projections; HAVING via WITH.
List: size head last tail reverse range
Path/metadata: length nodes relationships pathPairs labels type startNode endNode keys properties
id / elementIdReturns the IRI string.
Temporal accessors <date>.year/.month/.day/.hour/.minute/.second
Temporal constructors date() datetime() duration()Use XSD-typed literals.
Spatial point() / distance()

Null & type semantics

AspectStatusNotes
Three-valued logic in WHERE / filtersUnbound comparison → filter-false.
Null propagation through arithmetic / functions
IS NULL for absent property (nullable accessor)OPTIONAL-wrapped accessor.
Mixed-representation equality (encoded vs decoded)Normalized at DISTINCT/GROUP BY/join/MINUS/VALUES.
xsd:float string-backed numeric coercionIn SUM/AVG, comparisons, math.
List / map ordering in ORDER BYORDER BY <list/map> rejected (defensive total order internally).

Maintaining this matrix

This is a hand-maintained matrix, not yet a TCK-driven report. When a Cypher feature lands or a divergence changes:

  1. Update the relevant row here and the concept doc.
  2. Prefer ⟂ over ⏳ when the divergence is an intentional RDF-model choice — and record why in the Notes column.

A future step is to wire a subset of the openCypher TCK .feature scenarios as executable tests and generate the supported/deferred columns from their pass/fail results, replacing the hand-maintained status marks.