Expressions and Functions
This section describes the expressions and functions available in ArcadeDB’s OpenCypher implementation.
Literals
| Type | Syntax | Example |
|---|---|---|
Integer |
|
|
Float |
|
|
String |
|
|
Boolean |
|
|
Null |
|
|
List |
|
|
Map |
|
|
Property Access
Access properties using dot notation:
MATCH (p:Person)
RETURN p.name, p.age, p.address.city
CASE Expression
Conditional logic in expressions.
Simple CASE:
MATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN 'minor'
WHEN p.age < 65 THEN 'adult'
ELSE 'senior'
END AS ageGroup
Extended CASE:
MATCH (p:Person)
RETURN p.name,
CASE p.status
WHEN 'A' THEN 'Active'
WHEN 'I' THEN 'Inactive'
ELSE 'Unknown'
END AS statusName
A WHEN can hold several comma-separated values, a list or map literal, or a comparison applied to the tested value (since v26.10.1):
RETURN CASE x WHEN 1, 2 THEN 'low' WHEN > 5 THEN 'high' WHEN IS NULL THEN 'none' ELSE 'mid' END
RETURN CASE n.tags WHEN [] THEN 'empty' WHEN ['a'] THEN 'just a' ELSE 'other' END
Arithmetic Expressions
Perform arithmetic operations on numeric values.
| Operator | Description | Example |
|---|---|---|
|
Addition |
|
|
Subtraction |
|
|
Multiplication |
|
|
Division |
|
|
Modulo |
|
|
Power |
|
Examples with properties:
// Double a person's age
MATCH (p:Person)
RETURN p.name, p.age * 2 AS doubleAge
// Calculate monthly salary
MATCH (p:Person)
RETURN p.name, p.salary / 12 AS monthlySalary
// Complex expression
MATCH (p:Person)
RETURN p.name, (p.age * 2) + 10 AS result
The + operator also works for string concatenation: RETURN 'Hello' + ' ' + 'World' returns 'Hello World'.
|
Map Literals
Create inline map (object) structures.
Syntax: {key1: value1, key2: value2, …}
// Simple map literal
RETURN {name: 'Alice', age: 30}
// Empty map
RETURN {}
// Map with expressions
MATCH (p:Person)
WHERE p.name = 'Alice'
RETURN {
personName: p.name,
doubled: p.age * 2,
nextYearAge: p.age + 1
} AS info
List Comprehensions
Transform and filter lists using comprehension syntax.
Syntax: [variable IN list WHERE condition | expression]
-
variable- iteration variable -
list- source list or range -
WHERE condition- optional filter (elements that don’t match are excluded) -
| expression- optional mapping expression (transforms each element)
Basic transformation:
// Double each number
RETURN [x IN [1, 2, 3] | x * 2]
// Returns [2, 4, 6]
// Square numbers using range
RETURN [x IN range(1, 5) | x * x]
// Returns [1, 4, 9, 16, 25]
With filter:
// Filter and transform
RETURN [x IN [1, 2, 3, 4, 5] WHERE x > 2 | x * 10]
// Returns [30, 40, 50]
// Filter only (no transformation)
RETURN [x IN [1, 2, 3, 4, 5] WHERE x > 3]
// Returns [4, 5]
Map Projections
Extract specific properties from nodes or maps into a new map structure.
Syntax: variable{.property1, .property2, key: expression, .*}
-
.property- include this property from the source -
key: expression- add a computed value with the given key -
.*- include all properties from the source
Property selection:
// Select specific properties
MATCH (p:Person)
WHERE p.name = 'Alice'
RETURN p{.name, .age} AS person
// Returns {name: 'Alice', age: 30}
With computed values:
// Add computed properties
MATCH (p:Person)
WHERE p.name = 'Alice'
RETURN p{.name, doubleAge: p.age * 2} AS person
// Returns {name: 'Alice', doubleAge: 60}
All properties:
// Include all properties
MATCH (p:Person)
WHERE p.name = 'Alice'
RETURN p{.*} AS person
// Returns {name: 'Alice', age: 30, salary: 50000}
Aggregation Functions
String Functions
|
As of version 26.9.1, every STRING-typed argument of these functions (not just the first one) is validated: passing a non-STRING value - for example a list or a number - raises a client error instead of silently converting it to text. If you need to pass a non-string value on purpose, convert it explicitly first with |
substring()
Extracts a substring.
Syntax: substring(string, start [, length])
RETURN substring('hello world', 0, 5) // Returns 'hello'
RETURN substring('hello world', 6) // Returns 'world'
replace()
Replaces occurrences of a substring.
Syntax: replace(string, search, replacement)
RETURN replace('hello', 'l', 'L') // Returns 'heLLo'
split()
Splits a string into a list.
Syntax: split(string, delimiter)
RETURN split('a,b,c', ',') // Returns ['a', 'b', 'c']
left()
Returns the leftmost characters.
Syntax: left(string, length)
RETURN left('hello', 3) // Returns 'hel'
Math Functions
List Functions
size()
Returns the size of a list or string. A string is measured in Unicode characters, so an emoji counts as one (left(), right(), substring() and char_length() count the same way since v26.10.1).
RETURN size([1, 2, 3]) // Returns 3
RETURN size('hello') // Returns 5
Node and Relationship Functions
id()
Returns the internal ID of a node or relationship as a numeric (Long) value, matching the Neo4j semantics where id() returns an INTEGER. The two
components of an ArcadeDB RID (bucketId, position) are packed into a single non-negative number so it can be used in numeric predicates such as
WHERE id(n) >= 0.
MATCH (p:Person)
RETURN id(p) // Returns e.g. 786432
The packing uses 63 of the 64 bits of the Long (the sign bit is kept clear so the value is never negative): the upper bits hold the bucketId and the lower
bits the record position. The split is controlled by the global setting arcadedb.opencypher.idBucketBits (see Settings). The default of 16
reserves 16 bits for the bucketId (up to 65536 buckets) and 47 bits for the position (~1.4e14 records per bucket), which covers the vast majority of use cases:
-
Increase it (e.g.
20) for databases that allocate a very large number of buckets across many types. -
Decrease it for buckets that hold an extremely high number of records.
# Reserve 20 bits for the bucketId (up to ~1M buckets)
java ... -Darcadedb.opencypher.idBucketBits=20
To put the position field in perspective with the default 16: it reserves 47 bits, enough to address 2^47 ≈ 1.4e14 positions per bucket. A single bucket is
internally limited to ~2.1 billion pages (the page index is a 32-bit integer), so with a standard 64KB page holding on average ~500 records that is about
2.1e9 × 500 ≈ 1 trillion records (~137 TB) in one bucket - and the 47-bit position field covers that roughly 30x over. In other words, the numeric id()
never runs out of range before the bucket is physically full, so for almost every database the only setting worth tuning is the bucketId width (the bucket
count), not the position width.
|
|
From SQL, the numeric id can be resolved back to a record in O(1) with the cypherRID() function, e.g. SELECT FROM cypherRID(:id) (the inverse of id() / .asCypherRID()).
labels()
Returns the labels of a node as a list. A node created with no label at all (CREATE (n {name: 'x'})) returns an empty list, exactly like Neo4j — including when the label a node carries happens to be literally V or Vertex, which are ordinary, writable labels like any other and are not treated specially.
MATCH (p:Person)
RETURN labels(p) // Returns ['Person']
CREATE (n {name: 'x'})
RETURN labels(n) // Returns []
CREATE (n:V {name: 'y'})
RETURN labels(n) // Returns ['V']
|
Before version 26.9.1, "no labels" was represented internally by the type name There is no automatic per-record migration, because the two cases were indistinguishable under the old storage convention — an existing |
type()
Returns the type of a relationship. Calling it on a node is always an error.
MATCH (a)-[r]->(b)
RETURN type(r) // Returns e.g. 'KNOWS'
|
As of 26.9.1, this check (and the equivalent one for |
keys()
Returns the property keys of a node or relationship.
MATCH (p:Person {name: 'Alice', age: 30})
RETURN keys(p) // Returns ['name', 'age']
properties()
Returns all properties as a map.
MATCH (p:Person {name: 'Alice', age: 30})
RETURN properties(p) // Returns {name: 'Alice', age: 30}
Path Functions
Type Conversion Functions
SQL Function Bridge
ArcadeDB’s OpenCypher implementation includes a bridge to the ArcadeDB SQL functions. This provides access to 100+ additional functions. Some of them (such as sqrt(), date() and distance()) can be called by their bare name; others (such as pow(), uuid(), ifnull() and sysdate()) are not found unless you prefix them with sql., e.g. sql.pow(2, 8). When in doubt, use the sql. prefix.
Usage:
Call SQL functions directly in Cypher expressions:
// Date functions
MATCH (p:Person)
RETURN p.name, date() AS today
// Math functions
RETURN sql.pow(2, 8) AS result // Returns 256
// String functions
MATCH (p:Person)
RETURN normalize(p.name) AS normalizedName
// Geometric functions
RETURN distance(point(0, 0), point(3, 4)) AS dist // Returns ~555811.94 (meters)
point(0, 0) creates a WGS-84 point, so distance() returns a geodesic distance in meters, not the cartesian distance of 5.0.
The Cypher point() function returns a map with the coordinates, the crs name and the srid, as Neo4j does:
| Call | crs | srid |
|---|---|---|
|
cartesian |
7203 |
|
cartesian-3D |
9157 |
|
WGS-84 |
4326 |
|
WGS-84-3D |
4979 |
crs and srid can be given explicitly and are derived from each other. A pair that disagrees, or 2D/3D
coordinates that do not match the crs/srid, raise an error.
For a complete list of available SQL functions, see the SQL Functions reference.
Extended Functions (APOC Compatible)
ArcadeDB provides a comprehensive set of extended functions that are fully compatible with Neo4j’s APOC library. These functions work in both Cypher and SQL queries.
APOC Prefix Compatibility
ArcadeDB automatically supports the apoc. prefix. Your existing APOC queries work without modification:
// Neo4j/APOC style (works in ArcadeDB)
RETURN apoc.text.join(["a", "b", "c"], ",") AS result
// ArcadeDB native style (also works)
RETURN text.join(["a", "b", "c"], ",") AS result
Available Function Namespaces
| Namespace | Description |
|---|---|
|
Aggregation functions (median, statistics, etc.) |
|
Type conversion (toJson, fromJsonMap, etc.) |
|
Creation functions (uuid, virtual nodes/relationships) |
|
Date/time operations (format, parse, add, etc.) |
|
Map operations (merge, flatten, groupBy, etc.) |
|
Mathematical functions (sigmoid, tanh, etc.) |
|
Node operations (degree, labels, etc.) |
|
Path operations (create, combine, slice, expand) |
|
Relationship operations (type, startNode, etc.) |
|
String manipulation (join, split, replace, camelCase, etc.) |
|
Utility functions (md5, sha256, compress, etc.) |
|
Vector/embedding operations (similarity, distance, quantization, neighbors) |
Available Procedures
| Procedure | Description |
|---|---|
|
Create or match relationships |
|
Create or match nodes |
|
Weighted shortest path |
|
A* pathfinding with heuristics |
|
Find all simple paths |
|
Expand paths from a node |
|
Get all reachable nodes |
|
Schema as virtual graph |
|
Detailed schema information |
|
Database statistics |
Examples:
// Text functions
RETURN text.camelCase("hello world") AS result
// Returns: "helloWorld"
// Map functions
RETURN map.merge({a: 1}, {b: 2, a: 3}) AS result
// Returns: {a: 3, b: 2}
// JSON conversion
RETURN convert.toJson({name: "Alice", age: 30}) AS json
// Returns: '{"name":"Alice","age":30}'
// Hashing
RETURN util.sha256("password") AS hash
// Statistics
RETURN agg.statistics([1, 2, 3, 4, 5]) AS stats
// Returns: {count: 5, min: 1.0, max: 5.0, sum: 15.0, mean: 3.0, ...}
// Graph algorithms
MATCH (a:City {name: 'NYC'}), (b:City {name: 'LA'})
CALL algo.dijkstra(a, b, 'ROAD', 'distance') YIELD path, weight
RETURN path, weight
For the complete function reference, see reference/extended-functions.adoc#extended-functions.
Operators
Comparison Operators
| Operator | Description | Example |
|---|---|---|
|
Equal |
|
|
Not equal |
|
|
Less than |
|
|
Greater than |
|
|
Less than or equal |
|
|
Greater than or equal |
|
Logical Operators
| Operator | Description | Example |
|---|---|---|
|
Logical AND |
|
|
Logical OR |
|
|
Logical NOT |
|
String Operators
| Operator | Description | Example |
|---|---|---|
|
String prefix match. Uses an ordered index on the property, except a case insensitive one |
|
|
String suffix match |
|
|
Substring match |
|
|
Regular expression |
|
MATCH (n:Label) RETURN min(n.prop) (and max) reads one end of the index when prop has an ordered index on Label and the query has no other clause. A WHERE that is only a range of the same property (WHERE n.prop > 500000, <, >=, ⇐) reads the first matching entry of the range instead of the whole range. SQL does the same for SELECT min(prop) FROM Type WHERE prop > 500000. A range bound that is null matches nothing, with or without an index.
|
Parameters
Parameters allow you to pass values into queries without string concatenation, providing security and performance benefits.
Syntax: $parameterName
MATCH (p:Person)
WHERE p.name = $name AND p.age >= $minAge
RETURN p
Java Example:
Map<String, Object> params = Map.of(
"name", "Alice",
"minAge", 25
);
ResultSet result = database.query("opencypher",
"MATCH (p:Person) WHERE p.name = $name AND p.age >= $minAge RETURN p",
params);
Parameters are particularly useful for:
-
Preventing injection attacks
-
Query plan caching (same plan reused for different parameter values)
-
Handling special characters in values