The Cypher dialect
Which Cypher relate speaks, from the openCypher floor to GQL-aligned Cypher 25, how preview features opt in, and why some behavior departs from Neo4j.
cypherclipythonnodebrowser
Which Cypher does relate speak? openCypher The open specification of Cypher and its Technology Compatibility Kit (TCK) of scenarios; relate treats it as the floor of its dialect. Read more in the glossary is the floor: the openCypher Technology Compatibility Kit (TCK) is the language contract. On top of it sits Neo4j's Cypher 25 The Neo4j Cypher version aligned with GQL; the default language of every relate namespace, with CYPHER 5 as the per-statement alternative. Read more in the glossary, the version aligned with the ISO GQL standard, and it is the default. Newer syntax that Neo4j has accepted but not settled waits behind an opt-in, RELATE_PREVIEW. Where a feature only makes sense on a multi-user server, relate leaves it out and says so with a typed error.
Three layers
In words: openCypher, measured by the TCK, is the floor. Cypher 25 and its GQL-aligned syntax sit on top and are on by default. Preview features sit above that and run only when RELATE_PREVIEW names them. Server-only features, such as multiple databases and access control, are outside the dialect and fail with a typed error.
The examples on this page run on the docs' example graph: three people, two projects, who works on what and who knows whom.
CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
(grace:Person {name: 'Grace', role: 'engineer'}),
(noor:Person {name: 'Noor', role: 'maintainer'}),
(relate:Project {name: 'relate', language: 'Rust'}),
(tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
(ada)-[:WORKS_ON {since: 2024}]->(relate),
(grace)-[:WORKS_ON {since: 2023}]->(relate),
(grace)-[:WORKS_ON {since: 2022}]->(tck),
(noor)-[:MAINTAINS]->(relate),
(ada)-[:KNOWS]->(grace)(no rows) · nodes created: 5, relationships created: 5, properties set: 13, labels added: 5
openCypher as the floor
The TCK is a suite of scenarios that pins down what each openCypher clause, expression, and error means. relate treats it as the contract: parser, planner, executor, and error classes follow its scenarios. Coverage is measured by a separate spec harness, which runs Neo4j's Cypher suites, the openCypher TCK among them, against the build of each pull request that is ready for review, and reports the result. It reports; it does not block a merge, and no current pass count is published.
Until 2026-11-05 the engine ran a pinned copy of TCK 2024.3 in its own test suite, with a short list of documented deviations. That measurement is retired, so a figure from it is history, not a current claim.
Cypher 25 and GQL
Neo4j's Cypher 25 adds the syntax that brings Cypher toward GQL, and relate follows it. Label expressions combine labels with &, | and !, and IS can replace the colon:
MATCH (n IS Person|Project)
RETURN labels(n) AS labels, n.name AS name
ORDER BY name| labels | name |
|---|---|
| ["Person"] | "Ada" |
| ["Person"] | "Grace" |
| ["Person"] | "Noor" |
| ["Project"] | "opencypher-tck" |
| ["Project"] | "relate" |
5 rows
| labels | name | |-------------|------------------| | ["Person"] | "Ada" | | ["Person"] | "Grace" | | ["Person"] | "Noor" | | ["Project"] | "opencypher-tck" | | ["Project"] | "relate" | 5 rows
Path selectors and quantified relationships find routes without a hop bound written by hand. The shortest route from Ada to the TCK project, over either relationship type, in either direction:
MATCH p = SHORTEST 1 (:Person {name: 'Ada'})-[:KNOWS|WORKS_ON]-+(:Project {name: 'opencypher-tck'})
RETURN [n IN nodes(p) | n.name] AS route| route |
|---|
| ["Ada", "Grace", "opencypher-tck"] |
1 row
| route | |------------------------------------| | ["Ada", "Grace", "opencypher-tck"] | 1 row
LET binds a value without a WITH, and FILTER drops rows as a clause of its own:
MATCH (p:Person)-[w:WORKS_ON]->(r:Project)
LET years = 2026 - w.since
FILTER years >= 3
RETURN p.name AS person, r.name AS project, years
ORDER BY years DESC| person | project | years |
|---|---|---|
| "Grace" | "opencypher-tck" | 4 |
| "Grace" | "relate" | 3 |
2 rows
| person | project | years | |---------|------------------|-------| | "Grace" | "opencypher-tck" | 4 | | "Grace" | "relate" | 3 | 2 rows
The rest of the set includes NEXT to chain statements, COUNT { } and COLLECT { } subqueries, CALL (x) { } scope clauses, CALL { } IN TRANSACTIONS, dynamic labels with $(…), the vector and UUID types, graph types, and the SEARCH clause over vector and full-text indexes. The Cypher overview lists them all.
Where the proposals, Cypher 5 and Cypher 25 disagree, relate's target is Cypher 25 as Neo4j runs it. That is also why the retired TCK pin carried deviations: a few 2024.3 scenarios expect behavior that Cypher 25 changed on purpose. A statement can still ask for a version with a CYPHER 5 or CYPHER 25 prefix (Cypher versions has what each prefix changes). The default is recorded per namespace One of the three fixed graphs in every store: neo4j for user data, system for administration, and system_ontologies, listed but not queryable. Read more in the glossary in the store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary, and SHOW DATABASES reports it:
SHOW DATABASES YIELD name, defaultLanguage
RETURN name, defaultLanguage| name | defaultLanguage |
|---|---|
| "neo4j" | "CYPHER 25" |
| "system" | "CYPHER 25" |
| "system_ontologies" | "CYPHER 25" |
3 rows
| name | defaultLanguage | |---------------------|-----------------| | "neo4j" | "CYPHER 25" | | "system" | "CYPHER 25" | | "system_ontologies" | "CYPHER 25" | 3 rows
Preview features
A preview feature A Cypher feature from a not-yet-settled proposal that relate ships switched off; it runs only when RELATE_PREVIEW names it when the store opens. Read more in the glossary is a Cypher feature from a Cypher Improvement Proposal (CIP) that is not yet part of the settled dialect. relate implements several, and each runs only when the RELATE_PREVIEW environment variable names it. Without the opt-in, the statement fails before it runs:
RETURN datetime('2026-01-01T12:00+01:00') IS SAME INSTANT datetime('2026-01-01T11:00Z') AS sameerror: semantic error: SemanticError: preview feature 'chronological-comparisons' is not enabled: set RELATE_PREVIEW=chronological-comparisons
With the feature named in the environment of the process that opens the store, the same statement runs:
RELATE_PREVIEW=chronological-comparisons relate --mode envelope "RETURN datetime('2026-01-01T12:00+01:00') IS SAME INSTANT datetime('2026-01-01T11:00Z') AS same"{"columnTypes":["BOOLEAN"],"columns":["same"],"notifications":[],"planCache":"miss","rows":[{"same":true}],"timings":{"elapsed_ms":0.934375,"execute_ms":0.371625,"lex_parse_ms":0.028167,"lower_ms":0.428167,"parse_ms":0.49474999999999997,"plan_ms":0.057540999999999995,"result_generation_ms":0.00033299999999999996,"rewrite_ms":0.008042},"truncated":false}RELATE_PREVIEW takes a comma-separated list and is read once, when the store opens. An unknown name stops the open with an error that lists every known name, so a typo never passes silently. The names in this build are hybrid-search, local-callables, graph-type-opt-props, graph-type-default-values, graph-type-implied-labels, ontology, obfuscated-literals, mixed-label-syntax, float16-coordinate-types, chronological-comparisons, complex-search-patterns, coll-zip, coll-has and gql-stddev-aliases. Three of them, coll-zip, coll-has and gql-stddev-aliases, gate function names, and ontology gates the db.ontology.* procedures; the rest gate syntax. While one of the three function gates is off, a call fails as an unknown function.
The opt-in exists because a preview's shape can still change. Keeping it off by default means a statement that runs today keeps meaning the same thing when the proposal settles.
Where relate departs from Neo4j, and why
Most departures come from relate being an embedded, single-process engine. Server features have nothing to act on: there is one user graph per store, no users or roles, and no cluster. So a store holds three fixed namespaces: neo4j for data, system, and system_ontologies, which is listed but not queryable. USE routes a statement to neo4j or system. Any other graph name fails before the statement runs:
USE friends
MATCH (p:Person)
RETURN p.name AS nameerror: semantic error: DatabaseNotFound: Graph not found: friends
Database administration follows Neo4j Community Edition: SHOW DATABASES and setting the default language work, and the other administration commands fail with Unsupported administration command. A few departures are safety limits: an explicit variable-length bound of more than 500 hops is refused at admission unless --max-traversal-depth raises it, because a deeper walk can overflow the native stack.
The differences from Neo4j table lists each one with the error it produces and what to write instead.
Trade-offs
Following Cypher 25 gives relate one target, the Cypher that current Neo4j runs, so statements and documentation written for it carry over, apart from the departures in the differences table. The cost is that relate inherits Cypher 25's deliberate breaks from older openCypher, which is why the floor is described as measured coverage rather than a single pass figure. Gating previews behind a named opt-in costs one environment variable, and buys statements whose meaning does not shift under them.
Where this shows up
- Cypher overview: the clauses, expressions, and features relate supports.
- Differences from Neo4j: each departure, its error, and the replacement.
- Search: the
SEARCHclause and its preview forms. - Limitations: namespaces, nesting, and traversal limits.
- Search by vector similarity: Cypher 25's vector type and indexes in use.