Cypher Reference
GraphForge implements the full openCypher language on the v0.5.0 Rust core. Run
queries through forge.execute(query, params=None) — every call returns a
PyArrow Table (no CypherValue wrappers).
This guide covers everyday clauses, patterns, expressions, and functions. For install and first-graph steps, start with Quick Start; for programmatic construction, see Graph Construction.
Compliance: See TCK Compliance for the current openCypher TCK corpus gate.
Table of Contents
Section titled “Table of Contents”- Reading Data
- Writing Data
- Updating Data
- Query Chaining
- Patterns
- Expressions and Operators
- Functions
- Temporal Types
- Parameters
Reading Data
Section titled “Reading Data”Find nodes and relationships matching a pattern.
-- All nodesMATCH (n) RETURN n
-- Nodes with labelMATCH (p:Person) RETURN p
-- Nodes with multiple labelsMATCH (e:Person:Employee) RETURN e
-- Nodes with property filterMATCH (p:Person {name: 'Alice'}) RETURN p
-- Directed relationshipMATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a, b
-- Undirected relationshipMATCH (a:Person)-[:KNOWS]-(b:Person) RETURN a, b
-- Multiple relationship typesMATCH (a)-[r:KNOWS|LIKES]->(b) RETURN a, r, b
-- Variable-length pathsMATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a, b
-- Unbounded variable-lengthMATCH (a)-[*]->(b) RETURN a, b
-- Named pathMATCH p = (a:Person)-[:KNOWS*]->(b:Person) RETURN p
-- Multi-hopMATCH (a:Person)-[:KNOWS]->(b:Person)-[:WORKS_AT]->(c:Company)RETURN a.name, b.name, c.nameOPTIONAL MATCH
Section titled “OPTIONAL MATCH”Left-outer-join semantics: returns NULL for missing matches.
MATCH (p:Person)OPTIONAL MATCH (p)-[:KNOWS]->(friend:Person)RETURN p.name AS person, friend.name AS friend -- friend.name is NULL if no match
-- Multiple optional matchesMATCH (p:Person)OPTIONAL MATCH (p)-[:WORKS_AT]->(company:Company)OPTIONAL MATCH (p)-[:LIVES_IN]->(city:City)RETURN p.name, company.name, city.nameFilter by any boolean expression.
-- ComparisonMATCH (p:Person) WHERE p.age > 25 RETURN p
-- NULL checkMATCH (p:Person) WHERE p.email IS NULL RETURN pMATCH (p:Person) WHERE p.email IS NOT NULL RETURN p
-- Boolean operatorsMATCH (p:Person) WHERE p.age > 25 AND p.city = 'NYC' RETURN pMATCH (p:Person) WHERE p.age < 20 OR p.age > 60 RETURN pMATCH (p:Person) WHERE NOT p.active RETURN p
-- Label predicateMATCH (n) WHERE n:Person RETURN n
-- String predicatesMATCH (p:Person) WHERE p.name STARTS WITH 'Al' RETURN pMATCH (p:Person) WHERE p.name ENDS WITH 'ice' RETURN pMATCH (p:Person) WHERE p.name CONTAINS 'lic' RETURN p
-- RegexMATCH (p:Person) WHERE p.name =~ 'A.*' RETURN p
-- List membershipMATCH (p:Person) WHERE p.city IN ['NYC', 'Boston', 'London'] RETURN p
-- Pattern predicate (not returning path)MATCH (p:Person) WHERE (p)-[:KNOWS]->(:Person {name: 'Alice'}) RETURN pMATCH (p:Person) WHERE NOT (p)-[:KNOWS]->() RETURN p
-- EXISTS subqueryMATCH (p:Person)WHERE EXISTS { MATCH (p)-[:KNOWS]->(:Person) }RETURN p.name
-- Property existence (short form)MATCH (p:Person) WHERE exists(p.email) RETURN pRETURN
Section titled “RETURN”Project variables, properties, expressions, and aggregations.
-- Return variableMATCH (p:Person) RETURN p
-- Return propertyMATCH (p:Person) RETURN p.name, p.age
-- AliasMATCH (p:Person) RETURN p.name AS person, p.age AS age
-- DISTINCTMATCH (p:Person) RETURN DISTINCT p.city
-- Aggregation (implicit GROUP BY on non-aggregated columns)MATCH (p:Person) RETURN p.city AS city, count(*) AS total
-- All properties (map)MATCH (p:Person) RETURN p {.*}
-- Arithmetic in returnMATCH (p:Person) RETURN p.name, p.salary * 1.1 AS after_raise
-- CASE expressionMATCH (p:Person)RETURN p.name, CASE WHEN p.age < 18 THEN 'Minor' WHEN p.age < 65 THEN 'Adult' ELSE 'Senior' END AS category
-- Return * (all variables in scope)MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN *ORDER BY, LIMIT, SKIP
Section titled “ORDER BY, LIMIT, SKIP”-- Sort ascending (default)MATCH (p:Person) RETURN p.name ORDER BY p.age
-- Sort descendingMATCH (p:Person) RETURN p.name ORDER BY p.age DESC
-- Multiple sort keysMATCH (p:Person) RETURN p.name ORDER BY p.city ASC, p.age DESC
-- Limit resultsMATCH (p:Person) RETURN p.name ORDER BY p.age LIMIT 10
-- PaginationMATCH (p:Person) RETURN p.name ORDER BY p.name SKIP 20 LIMIT 10Writing Data
Section titled “Writing Data”CREATE
Section titled “CREATE”Create nodes and relationships.
-- Single nodeCREATE (p:Person {name: 'Alice', age: 30})
-- Multiple labelsCREATE (e:Person:Employee {name: 'Charlie'})
-- Multiple nodes in one clauseCREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
-- Relationship between existing nodesMATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})CREATE (a)-[:KNOWS {since: 2020}]->(b)
-- Create inlineCREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})
-- CREATE ... RETURNCREATE (p:Person {name: 'Alice'})RETURN p.name AS name, id(p) AS node_idFind or create — safe for idempotent upserts.
-- Merge node (create if not exists, match if exists)MERGE (p:Person {email: 'alice@example.com'})
-- ON CREATE and ON MATCH callbacksMERGE (p:Person {email: 'alice@example.com'})ON CREATE SET p.created = 2024, p.name = 'Alice'ON MATCH SET p.last_seen = 2024
-- Merge relationship (both endpoints must already exist)MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})MERGE (a)-[:KNOWS]->(b)
-- Merge with RETURNMERGE (p:Person {email: 'alice@example.com'})RETURN pUNWIND
Section titled “UNWIND”Expand a list into rows.
UNWIND [1, 2, 3] AS x RETURN x * 2 AS doubled
-- Common pattern: batch-create from listUNWIND ['Alice', 'Bob', 'Charlie'] AS nameCREATE (:Person {name: name})
-- Expand collected listMATCH (p:Person)WITH collect(p.name) AS namesUNWIND names AS nameRETURN nameUpdating Data
Section titled “Updating Data”Add or update properties and labels.
-- Set propertyMATCH (p:Person {name: 'Alice'}) SET p.age = 31
-- Multiple propertiesMATCH (p:Person {name: 'Alice'})SET p.age = 31, p.city = 'Boston', p.active = true
-- Set from mapMATCH (p:Person {name: 'Alice'})SET p += {age: 31, city: 'Boston'}
-- Overwrite all propertiesMATCH (p:Person {name: 'Alice'})SET p = {name: 'Alice', age: 31}
-- Add labelMATCH (p:Person {name: 'Alice'}) SET p:Manager
-- SET ... RETURNMATCH (p:Person {name: 'Alice'})SET p.age = 31RETURN p.name AS name, p.age AS new_ageREMOVE
Section titled “REMOVE”Remove properties and labels.
-- Remove propertyMATCH (p:Person {name: 'Alice'}) REMOVE p.temp
-- Remove multipleMATCH (p:Person {name: 'Alice'}) REMOVE p.temp, p.draft
-- Remove labelMATCH (p:Person {name: 'Alice'}) REMOVE p:ManagerDELETE / DETACH DELETE
Section titled “DELETE / DETACH DELETE”-- Delete a relationshipMATCH (a)-[r:KNOWS]->(b) WHERE a.name = 'Alice' DELETE r
-- Delete a node (no relationships may exist)MATCH (p:Person {name: 'Alice'}) DELETE p
-- DETACH DELETE: remove node and all its relationshipsMATCH (p:Person {name: 'Alice'}) DETACH DELETE p
-- Delete everythingMATCH (n) DETACH DELETE nQuery Chaining
Section titled “Query Chaining”Pass results between query stages, filter aggregations, limit scope.
-- Chain match with filterMATCH (p:Person)-[:KNOWS]->(friend)WITH p, count(friend) AS friendsWHERE friends > 5RETURN p.name AS person, friends
-- Rename and continueMATCH (p:Person)WITH p.name AS name, p.age AS ageWHERE age > 25RETURN name, age
-- Aggregate then matchMATCH (p:Person)WITH p.city AS city, count(*) AS popWHERE pop > 100MATCH (p2:Person {city: city})RETURN p2.name, city, popORDER BY pop DESC
-- WITH DISTINCTMATCH (p:Person)-[:KNOWS]->(friend:Person)WITH DISTINCT friendRETURN friend.name AS mutual_friendUNION / UNION ALL
Section titled “UNION / UNION ALL”-- UNION (removes duplicates)MATCH (p:Person) RETURN p.name AS nameUNIONMATCH (c:Company) RETURN c.name AS name
-- UNION ALL (keeps duplicates)MATCH (p:Person) RETURN p.name AS nameUNION ALLMATCH (p2:Person) RETURN p2.name AS namePatterns
Section titled “Patterns”Node Patterns
Section titled “Node Patterns”| Pattern | Meaning |
|---|---|
(n) |
Any node |
(n:Person) |
Node with label Person |
(n:Person:Employee) |
Node with both labels |
(n:Person {age: 30}) |
Node with label and property |
(:Person) |
Anonymous node with label |
({name: 'Alice'}) |
Anonymous node with property |
Relationship Patterns
Section titled “Relationship Patterns”| Pattern | Meaning |
|---|---|
-[r]-> |
Any directed relationship |
-[:KNOWS]-> |
Specific type |
-[r:KNOWS {since: 2020}]-> |
Type with property |
-[:KNOWS|LIKES]-> |
Multiple types (OR) |
-[*]-> |
Variable-length (any) |
-[*2]-> |
Exactly 2 hops |
-[*1..3]-> |
1 to 3 hops |
-[*..5]-> |
Up to 5 hops |
-[*3..]-> |
3 or more hops |
Path Variables
Section titled “Path Variables”-- Bind path to variableMATCH p = (a:Person)-[:KNOWS*]->(b:Person)RETURN length(p) AS hops, nodes(p) AS path_nodes
-- Shortest pathMATCH p = shortestPath((a:Person {name: 'Alice'})-[:KNOWS*]->(b:Person {name: 'Bob'}))RETURN pExpressions and Operators
Section titled “Expressions and Operators”Arithmetic
Section titled “Arithmetic”RETURN 5 + 3 -- 8RETURN 5 - 3 -- 2RETURN 5 * 3 -- 15RETURN 5 / 2 -- 2.5RETURN 5 % 2 -- 1RETURN 2 ^ 10 -- 1024.0Comparison
Section titled “Comparison”RETURN 5 = 5 -- trueRETURN 5 <> 6 -- trueRETURN 5 < 6 -- trueRETURN 5 <= 5 -- trueRETURN 6 > 5 -- trueRETURN 6 >= 6 -- trueBoolean
Section titled “Boolean”RETURN true AND false -- falseRETURN true OR false -- trueRETURN NOT true -- falseRETURN true XOR true -- false
-- NULL propagationRETURN null AND true -- nullRETURN null OR true -- trueRETURN NOT null -- nullString
Section titled “String”RETURN 'hello' + ' world' -- 'hello world'RETURN 'Alice' STARTS WITH 'Al' -- trueRETURN 'Alice' ENDS WITH 'ice' -- trueRETURN 'Alice' CONTAINS 'lic' -- trueRETURN 'Alice' =~ 'A.*' -- true (regex)List Operators
Section titled “List Operators”RETURN [1, 2, 3] + [4, 5] -- [1, 2, 3, 4, 5]RETURN 3 IN [1, 2, 3] -- trueRETURN [1, 2, 3][0] -- 1RETURN [1, 2, 3][1..3] -- [2, 3]-- NULL propagates through most operationsRETURN null + 5 -- nullRETURN null = null -- null (use IS NULL instead)
-- Safe operatorsMATCH (p:Person) WHERE p.age IS NULL RETURN pMATCH (p:Person) WHERE p.age IS NOT NULL RETURN pCASE Expression
Section titled “CASE Expression”-- Simple formRETURN CASE p.status WHEN 'active' THEN 'Active User' WHEN 'inactive' THEN 'Inactive User' ELSE 'Unknown'END
-- Generic formRETURN CASE WHEN p.age < 18 THEN 'Minor' WHEN p.age < 65 THEN 'Adult' ELSE 'Senior'ENDFunctions
Section titled “Functions”String Functions
Section titled “String Functions”| Function | Example | Result |
|---|---|---|
toLower(s) |
toLower('HELLO') |
'hello' |
toUpper(s) |
toUpper('hello') |
'HELLO' |
trim(s) |
trim(' hi ') |
'hi' |
ltrim(s) |
ltrim(' hi') |
'hi' |
rtrim(s) |
rtrim('hi ') |
'hi' |
replace(s, f, r) |
replace('aaa', 'a', 'b') |
'bbb' |
substring(s, start, len) |
substring('hello', 1, 3) |
'ell' |
left(s, n) |
left('hello', 2) |
'he' |
right(s, n) |
right('hello', 2) |
'lo' |
split(s, delim) |
split('a,b,c', ',') |
['a','b','c'] |
reverse(s) |
reverse('hello') |
'olleh' |
size(s) |
size('hello') |
5 |
toString(x) |
toString(42) |
'42' |
Math Functions
Section titled “Math Functions”| Function | Description |
|---|---|
abs(n) |
Absolute value |
ceil(n) |
Round up |
floor(n) |
Round down |
round(n) |
Round to nearest |
sqrt(n) |
Square root |
pow(base, exp) |
Power |
exp(n) |
e^n |
log(n) |
Natural log |
log10(n) |
Base-10 log |
sin(n), cos(n), tan(n) |
Trig (radians) |
asin(n), acos(n), atan(n), atan2(y,x) |
Inverse trig |
pi() |
π (3.14159…) |
e() |
e (2.71828…) |
rand() |
Random 0.0–1.0 |
sign(n) |
-1, 0, or 1 |
List Functions
Section titled “List Functions”| Function | Example | Result |
|---|---|---|
head(list) |
head([1,2,3]) |
1 |
tail(list) |
tail([1,2,3]) |
[2,3] |
last(list) |
last([1,2,3]) |
3 |
size(list) |
size([1,2,3]) |
3 |
range(start, end) |
range(1,5) |
[1,2,3,4,5] |
range(start, end, step) |
range(0,10,2) |
[0,2,4,6,8,10] |
reverse(list) |
reverse([1,2,3]) |
[3,2,1] |
sort(list) |
sort([3,1,2]) |
[1,2,3] |
keys(map) |
keys({a:1,b:2}) |
['a','b'] |
List Comprehension and Predicates
Section titled “List Comprehension and Predicates”-- Filter a listRETURN [x IN range(1,10) WHERE x % 2 = 0] -- [2,4,6,8,10]
-- Transform a listRETURN [x IN range(1,5) | x * x] -- [1,4,9,16,25]
-- Filter + transformRETURN [x IN range(1,10) WHERE x > 5 | x * 2] -- [12,14,16,18,20]
-- all() — every element satisfies predicateRETURN all(x IN [2,4,6] WHERE x % 2 = 0) -- true
-- any() — at least one element satisfies predicateRETURN any(x IN [1,2,3] WHERE x > 2) -- true
-- none() — no element satisfies predicateRETURN none(x IN [1,2,3] WHERE x > 5) -- true
-- single() — exactly one element satisfies predicateRETURN single(x IN [1,2,3] WHERE x = 2) -- true
-- reduce() — fold list to single valueRETURN reduce(acc = 0, x IN [1,2,3,4,5] | acc + x) -- 15Aggregation Functions
Section titled “Aggregation Functions”-- Count rowsMATCH (p:Person) RETURN count(*) AS total
-- Count non-nullMATCH (p:Person) RETURN count(p.age) AS with_age
-- Count distinctMATCH (p:Person) RETURN count(DISTINCT p.city) AS cities
-- Numeric aggregationsMATCH (p:Person) RETURN sum(p.salary), avg(p.age), min(p.age), max(p.age)
-- Collect into listMATCH (p:Person) RETURN collect(p.name) AS names
-- Collect distinctMATCH (p:Person) RETURN collect(DISTINCT p.city) AS cities
-- Standard deviationMATCH (p:Person) RETURN stDev(p.age), stDevP(p.age)
-- PercentileMATCH (p:Person) RETURN percentileDisc(p.age, 0.5) AS medianGraph Functions
Section titled “Graph Functions”-- Node identityRETURN id(n)
-- LabelsRETURN labels(n) -- list of labelsRETURN 'Person' IN labels(n) -- true/false
-- Relationship typeRETURN type(r)
-- Properties (as map)RETURN properties(n)
-- Keys (property names)RETURN keys(n)
-- Path functionsMATCH p = (a)-[*]->(b)RETURN nodes(p), relationships(p), length(p)
-- EndpointsMATCH (a)-[r]->(b)RETURN startNode(r), endNode(r)
-- DegreeMATCH (n:Person)RETURN size((n)-[:KNOWS]->()) AS out_degreePredicate Functions
Section titled “Predicate Functions”-- exists() — property existsMATCH (p:Person) WHERE exists(p.email) RETURN p
-- isEmpty()RETURN isEmpty([]) -- trueRETURN isEmpty('') -- trueRETURN isEmpty(null) -- true
-- Null coalescingRETURN coalesce(null, null, 'default') -- 'default'RETURN coalesce(p.nickname, p.name) -- first non-nullConversion Functions
Section titled “Conversion Functions”RETURN toInteger('42') -- 42RETURN toInteger(3.7) -- 3RETURN toFloat('3.14') -- 3.14RETURN toString(42) -- '42'RETURN toBoolean('true') -- trueRETURN toBoolean(0) -- falseTemporal Types
Section titled “Temporal Types”GraphForge implements full openCypher temporal precision including nanoseconds and IANA timezone names.
Construction
Section titled “Construction”RETURN date('2024-01-15')RETURN date({year: 2024, month: 1, day: 15})RETURN date() -- current date
RETURN time('14:30:00.000000789') -- with nanosecondsRETURN time({hour: 14, minute: 30, second: 0, nanosecond: 789})
RETURN datetime('2024-01-15T14:30:00[Europe/London]') -- IANA timezoneRETURN datetime('2024-01-15T14:30:00+01:00') -- offsetRETURN datetime() -- current datetime
RETURN localDatetime('2024-01-15T14:30:00')RETURN duration('P1Y2M3DT4H5M6.789S')RETURN duration({years: 1, months: 2, days: 3})Accessors
Section titled “Accessors”RETURN date('2024-01-15').year -- 2024RETURN date('2024-01-15').month -- 1RETURN date('2024-01-15').day -- 15RETURN date('2024-01-15').weekday -- 1 (Monday)RETURN date('2024-01-15').week -- 3 (ISO week)RETURN date('2024-01-15').ordinalDay -- 15
RETURN time('14:30:00.000000789').hour -- 14RETURN time('14:30:00.000000789').nanosecond -- 789
RETURN datetime('2024-01-15T14:30:00[Europe/Stockholm]').timezone-- 'Europe/Stockholm'
RETURN duration('P1Y2M3DT4H5M6S').years -- 1RETURN duration('P1Y2M3DT4H5M6S').months -- 2RETURN duration('PT0.000000789S').nanoseconds -- 789Arithmetic
Section titled “Arithmetic”RETURN date('2024-01-01') + duration('P1M') -- 2024-02-01RETURN date('2024-01-01') - duration('P1Y') -- 2023-01-01RETURN duration('P1Y') + duration('P6M') -- P1Y6M
-- BetweenRETURN duration.between(date('2020-01-01'), date('2024-01-01'))RETURN duration.inMonths(date('2020-01-01'), date('2024-01-01'))RETURN duration.inDays(datetime('2020-01-01T00:00'), datetime('2020-01-15T12:00'))RETURN duration.inSeconds(time('08:00'), time('16:30'))Extreme Years
Section titled “Extreme Years”GraphForge supports years outside Python’s native range (1–9999):
RETURN localdatetime('+999999999-12-31T23:59:59').year -- 999999999RETURN localdatetime('-000001-01-01T00:00').year -- -1Truncation
Section titled “Truncation”RETURN date.truncate('month', date('2024-07-15')) -- 2024-07-01RETURN datetime.truncate('day', datetime()) -- current day at midnightRETURN time.truncate('hour', time('14:37:00')) -- 14:00:00Parameters
Section titled “Parameters”Bind values as parameters to avoid string interpolation:
# Pythontable = forge.execute( "MATCH (p:Person {name: $name}) WHERE p.age > $min_age RETURN p", {"name": "Alice", "min_age": 25},)-- In Cypher, $name and $min_age are the parameter syntaxMATCH (p:Person {name: $name})WHERE p.age > $min_ageRETURN p.name, p.ageParameters work with all value types: strings, integers, floats, booleans, null, lists, maps.
Common Patterns
Section titled “Common Patterns”Find Friends-of-Friends
Section titled “Find Friends-of-Friends”MATCH (me:Person {name: 'Alice'})-[:KNOWS]->(friend)-[:KNOWS]->(foaf:Person)WHERE NOT (me)-[:KNOWS]->(foaf) AND me <> foafRETURN DISTINCT foaf.name AS recommendationMost Connected Nodes
Section titled “Most Connected Nodes”MATCH (p:Person)-[:KNOWS]->(friend)RETURN p.name AS person, count(friend) AS connectionsORDER BY connections DESCLIMIT 10Upsert Pattern
Section titled “Upsert Pattern”MERGE (p:Person {email: 'alice@example.com'})ON CREATE SET p.name = 'Alice', p.created = 2024ON MATCH SET p.last_seen = 2024RETURN pBatch Create from List
Section titled “Batch Create from List”UNWIND [ {name: 'Alice', age: 30}, {name: 'Bob', age: 25}, {name: 'Carol', age: 35}] AS rowCREATE (:Person {name: row.name, age: row.age})Conditional Return
Section titled “Conditional Return”MATCH (p:Person)RETURN p.name, CASE WHEN p.age IS NULL THEN 'unknown' WHEN p.age < 18 THEN 'minor' ELSE 'adult' END AS categoryEXISTS Subquery
Section titled “EXISTS Subquery”-- Nodes that have at least one outgoing relationshipMATCH (p:Person)WHERE EXISTS { MATCH (p)-[:KNOWS]->() }RETURN p.name
-- Nodes that have no relationshipsMATCH (p:Person)WHERE NOT EXISTS { MATCH (p)-[]-() }RETURN p.name AS isolatedSee Also
Section titled “See Also”- Graph Construction — Python API for building graphs
- Quick Start — five-minute walkthrough
- OpenCypher Compatibility — feature matrix
- TCK Compliance — Rust v0.5 conformance status