Cypher Clauses
Reading Clauses
MATCH
Find patterns in the graph:
MATCH (n:Person) RETURN n
MATCH (a)-[:KNOWS]->(b) RETURN a, b
MATCH (n:Person {name: 'Alice'}) RETURN n
Shortest Path Patterns
Find shortest paths between nodes:
// Find a single shortest path
MATCH p = shortestPath((a:Person {name: 'Alice'})-[*]-(b:Person {name: 'Bob'}))
RETURN p, length(p)
// Find all shortest paths (all paths with minimum length)
MATCH p = allShortestPaths((a:Person)-[*]-(b:Person))
WHERE a.name = 'Alice' AND b.name = 'Bob'
RETURN p
// With relationship type filter
MATCH p = shortestPath((a)-[:KNOWS*]->(b))
RETURN nodes(p), relationships(p)
// With length constraints
MATCH p = shortestPath((a)-[*..10]->(b))
RETURN p
OPTIONAL MATCH
Like MATCH, but returns NULL for non-matches (left join semantics):
MATCH (p:Person)
OPTIONAL MATCH (p)-[:MANAGES]->(e)
RETURN p.name, e.name
WHERE
Filter results:
MATCH (n:Person)
WHERE n.age > 21 AND n.city = 'NYC'
RETURN n
Writing Clauses
CREATE
Create nodes and relationships:
CREATE (n:Person {name: 'Alice', age: 30})
CREATE (a)-[:KNOWS {since: 2020}]->(b)
MERGE
Create if not exists, match if exists:
MERGE (n:Person {name: 'Alice'})
ON CREATE SET n.created = timestamp()
ON MATCH SET n.updated = timestamp()
SET
Update properties:
MATCH (n:Person {name: 'Alice'})
SET n.age = 31, n.city = 'LA'
Add labels:
MATCH (n:Person {name: 'Alice'})
SET n:Employee
REMOVE
Remove properties:
MATCH (n:Person {name: 'Alice'})
REMOVE n.temporary_field
Remove labels:
MATCH (n:Person:Employee {name: 'Alice'})
REMOVE n:Employee
DELETE
Delete nodes (must have no relationships):
MATCH (n:Person {name: 'Alice'})
DELETE n
DETACH DELETE
Delete nodes and all their relationships:
MATCH (n:Person {name: 'Alice'})
DETACH DELETE n
Composing Clauses
WITH
Chain query parts, aggregation, and filtering:
MATCH (p:Person)-[:WORKS_AT]->(c:Company)
WITH c, count(p) as employee_count
WHERE employee_count > 10
RETURN c.name, employee_count
UNWIND
Expand a list into rows:
UNWIND [1, 2, 3] AS x
RETURN x
UNWIND $names AS name
CREATE (n:Person {name: name})
FOREACH
Iterate and perform updates:
MATCH p = (start)-[*]->(end)
FOREACH (n IN nodes(p) | SET n.visited = true)
LOAD CSV
Import data from CSV files:
// With headers (access columns by name)
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
CREATE (n:Person {name: row.name, age: toInteger(row.age)})
// Without headers (access columns by index)
LOAD CSV FROM 'file:///data.csv' AS row
CREATE (n:Item {id: row[0], value: row[1]})
// Custom field terminator
LOAD CSV WITH HEADERS FROM 'file:///data.tsv' AS row FIELDTERMINATOR '\t'
CREATE (n:Record {field1: row.col1})
Note: File paths are relative to the current working directory. Use file:/// prefix for local files.
Multi-Graph Queries
FROM Clause
Query specific graphs when using GraphManager (multi-graph support):
// Query a specific graph
MATCH (n:Person) FROM social
RETURN n.name
// Combined with other clauses
MATCH (p:Person) FROM social
WHERE p.age > 21
RETURN p.name, graph(p) AS source_graph
The graph() function returns which graph a node came from.
Combining Results
UNION
Combine results from multiple queries, removing duplicates:
MATCH (n:Person) WHERE n.city = 'NYC' RETURN n.name
UNION
MATCH (n:Person) WHERE n.age > 50 RETURN n.name
UNION ALL
Combine results keeping all rows (including duplicates):
MATCH (a:Person)-[:KNOWS]->(b) RETURN b.name AS connection
UNION ALL
MATCH (a:Person)-[:WORKS_WITH]->(b) RETURN b.name AS connection
Return Clause
RETURN
Specify what to return:
MATCH (n:Person) RETURN n
MATCH (n:Person) RETURN n.name, n.age
MATCH (n:Person) RETURN n.name AS name
DISTINCT
Remove duplicates:
MATCH (n:Person)-[:KNOWS]->(m)
RETURN DISTINCT m.city
ORDER BY
Sort results:
MATCH (n:Person)
RETURN n.name, n.age
ORDER BY n.age DESC, n.name ASC
SKIP and LIMIT
Pagination:
MATCH (n:Person)
RETURN n
ORDER BY n.name
SKIP 10
LIMIT 5
Aggregation
Use aggregate functions in RETURN or WITH:
MATCH (p:Person)-[:WORKS_AT]->(c:Company)
RETURN c.name, count(p), avg(p.salary), collect(p.name)
See Functions Reference for all aggregate functions.