Cypher Compatibility Matrix
This section provides a comprehensive overview of Cypher language features and their support status in ArcadeDB’s native OpenCypher implementation.
OpenCypher TCK Compliance
ArcadeDB’s OpenCypher implementation has been validated against the official OpenCypher Technology Compatibility Kit (TCK) version 9, developed by Neo4j and released under the Apache License 2.0.
| Metric | Value | Status |
|---|---|---|
TCK Pass Rate |
97.8% |
✅ Production Ready |
Total Scenarios |
3,897 |
Official OpenCypher v9 test suite |
Passed |
3,812 |
Full compatibility with core features |
Failed |
0 |
All executable tests pass |
Skipped |
85 |
Documented limitations (2.2%) |
|
Verify TCK Compliance Yourself To run the OpenCypher TCK tests on your system:
The TCK test suite is located at: For more information about the OpenCypher TCK, visit: https://github.com/opencypher/openCypher/tree/master/tck |
Known Limitations
The 85 skipped scenarios (2.2%) represent either architectural design choices or edge cases that rarely occur in production use. The following limitations affect 85 TCK scenarios (2.2%) and represent either design choices or rare edge cases.
1. Temporal Range Limitation (Java Platform Constraint)
TCK Impact: 4 scenarios (temporal sorting with extreme date ranges)
Reason: ArcadeDB uses Java’s LocalDateTime with nanosecond precision stored as a 64-bit long, limiting the date range to approximately 1677-2262.
Examples that don’t work:
// Year 0001 with nanoseconds - exceeds range
CREATE (:Event {datetime: localdatetime({year: 1, month: 1, day: 1,
hour: 1, minute: 1, second: 1,
nanosecond: 1})})
// Year 9999 with nanoseconds - exceeds range
CREATE (:FutureEvent {datetime: localdatetime({year: 9999, month: 9, day: 9,
hour: 9, minute: 59, second: 59,
nanosecond: 999999999})})
Workaround: Use dates within the 1677-2262 range (covers virtually all real-world data), or store historical dates as strings.
2. Case-Insensitive Identifiers (By Design)
TCK Impact: ~40 scenarios (case-sensitive type/property names)
Reason: ArcadeDB follows SQL conventions where identifiers are case-insensitive, providing consistency across query languages.
Examples that don’t work:
// These create a collision - T2 and t2 are considered the same type
CREATE ()-[:T2]->()
CREATE ()-[:t2]->() // ❌ Conflicts with :T2
Workaround: Use distinct type names (e.g., :Type2 and :Type2Alt).
3. Variable-Length Path with USING Clause (Advanced Feature)
TCK Impact: ~20 scenarios (VLP with relationship list constraints)
Reason: The USING clause for constraining VLP traversal to specific relationship lists is an advanced feature rarely used in production.
Examples that don’t work:
// Collect relationships, then traverse using only those relationships
MATCH ()-[rels]->()
MATCH (a)-[*2 USING rels]->(b) // ❌ USING clause not implemented
RETURN a, b
Workaround: Use standard VLP with relationship type filters:
MATCH (a)-[:TYPE*2]->(b) // ✅ Works - filters by type
RETURN a, b
4. Cross-Type Comparison Strictness (Semantic Edge Case)
TCK Impact: ~20 scenarios (incompatible type comparisons)
Reason: Comparing fundamentally different types (nodes vs strings, paths vs primitives) in production code typically indicates a query error.
Affected: Strict null-return semantics for cross-type comparisons.
5. User Functions Support
TCK Impact: 0 scenarios (extension feature)
ArcadeDB enhances OpenCypher with user-defined functions in 4 languages:
// Define in SQL
DEFINE FUNCTION math.sum "SELECT :a + :b" PARAMETERS [a,b] LANGUAGE sql
// Define in JavaScript
DEFINE FUNCTION js.greet "return 'Hello ' + name" PARAMETERS [name] LANGUAGE js
// Define in OpenCypher
DEFINE FUNCTION cypher.double "RETURN $x * 2" PARAMETERS [x] LANGUAGE opencypher
// Call from any query language
RETURN math.sum(3, 5) // Works in Cypher, SQL, Gremlin, etc.
See User Functions for complete documentation.
Not Implemented Features
The following Cypher features are not currently implemented:
-
Index hints:
USING INDEX n:Person(name)- Query planning is automatic via cost-based optimizer
Cypher 25 / GQL Strictness
Following ISO/IEC 39075 (GQL) and Cypher 25, ArcadeDB rejects removed legacy syntax with an actionable error that points at the supported replacement:
-
PERIODIC COMMITis removed — useCALL { … } IN TRANSACTIONS [OF n ROWS]for batched writes.USING PERIODIC COMMITreports an error with this hint. -
Legacy
{param}parameters are removed — use$param. The old curly-brace parameter form reports an error with this hint (map projections such asn{.name}and quantified path quantifiers such as{1,5}are unaffected). -
Ambiguous aggregation grouping is rejected — a
RETURN(orWITH) that mixes an aggregating expression with a non-grouping reference raisesAmbiguousAggregationExpression. Aggregations are not allowed inWHERE.
Three-Valued Logic (UNKNOWN)
ArcadeDB follows the GQL three-valued logic: the UNKNOWN truth value is represented as null in boolean context (no separate UNKNOWN value type). AND / OR / NOT / XOR propagate null, comparisons against null evaluate to null, and a null condition in WHERE is treated as non-matching, while RETURN preserves the null.
RETURN true AND null AS a // null
RETURN false AND null AS b // false
RETURN true OR null AS c // true
RETURN null = null AS d // null
The list predicates all(), any(), none() and single() follow the same rule: if the inner condition is
null for one or more elements and no element decides the result outright (a false for all(), a true for
any()/none()), the predicate itself evaluates to null rather than true/false. A definite mismatch always
wins over an unknown one, so a single element that decides the result short-circuits regardless of any `null`s
elsewhere in the list.
WITH [1, 2, null] AS list
RETURN any(x IN list WHERE x > 5) AS a // null: no match, but one candidate was unknown
RETURN none(x IN list WHERE x > 5) AS b // null: same reasoning
RETURN all(x IN list WHERE x IS NOT NULL) AS c // false: a definite mismatch always wins over unknown
Strict Numeric Types
The IS TYPED value-type predicate and CREATE CONSTRAINT … IS TYPED <type> honor the GQL numeric width hierarchy (INT8/INT16/INT32/INT64, FLOAT32/FLOAT64), mapping each onto a native ArcadeDB width so a declared property persists and reloads at that width. See IS TYPED data types for the full mapping.
shortestPath() and Hop Bounds
shortestPath() and allShortestPaths() honor the *min..max quantifier written on the relationship: a
shortest path outside that range is not an answer, and no longer path is substituted for it.
MATCH (a:City {name:'Rome'}), (b:City {name:'Oslo'}), p = shortestPath((a)-[:ROAD*..2]-(b))
RETURN length(p) // no row when the shortest Rome..Oslo path is longer than 2 hops
A relationship written without a quantifier, -[:ROAD]-, declares exactly one hop and is bounded accordingly.
When both endpoints resolve to the same vertex, the answer is the zero-length path, as in Neo4j. An explicitly written minimum above one hop rejects it, since zero hops is not in the declared range:
MATCH (a:City {name:'Rome'}), p = shortestPath((a)-[:ROAD*]-(a)) RETURN length(p) // 0
MATCH (a:City {name:'Rome'}), p = shortestPath((a)-[:ROAD*3..5]-(a)) RETURN length(p) // no row
Such a pattern is not reinterpreted as a request for the shortest cycle back to the start.
Indexes Inherited from a Parent Type
An index declared on a parent type serves queries on every child type, exactly as it does in SQL, so a single index at the top of a hierarchy is enough:
CREATE VERTEX TYPE Node;
CREATE VERTEX TYPE Entity EXTENDS Node;
CREATE PROPERTY Node.id STRING;
CREATE INDEX ON Node (id) UNIQUE;
MATCH (e:Entity) WHERE e.id = 'foo-1' RETURN e // uses the inherited Node[id] index
A child type cannot declare a second index on a property its parent already indexes, and does not need one: the parent’s index already covers the child’s records.
Architectural Differences from Neo4j
While implementing the OpenCypher standard, ArcadeDB differs from Neo4j in these intentional design choices:
-
Case-Insensitive Identifiers: Type names and property keys follow SQL conventions
-
Multi-Model Database: Supports Document, Key-Value, and Graph models in one database
-
Native Indexing: Uses LSM-Tree indexes optimized for write-heavy workloads
-
Query Language Flexibility: Supports SQL, Cypher, Gremlin, GraphQL, and MongoDB Query Language on the same data
-
User Functions: Dynamic functions in 4 languages (SQL, JavaScript, OpenCypher, Java) vs Neo4j’s Java-only compiled plugins