Cypher Tutorial
|
Prefer a guided, video-based walkthrough? The ArcadeDB Academy offers a free course on Cypher with hands-on exercises and a certification at the end. |
This tutorial introduces you to Cypher, a declarative graph query language. By the end of this tutorial, you’ll be able to create, query, and manipulate graph data in ArcadeDB using Cypher.
What is Cypher?
Cypher is a pattern-matching query language designed specifically for graphs.
It uses ASCII-art syntax to represent graph patterns, making queries intuitive and readable.
For example, (a)-[:KNOWS]→(b) visually represents a KNOWS relationship from node a to node b.
Setting Up
Before running Cypher queries, ensure you have:
-
ArcadeDB server running
-
A database created (we’ll use
tutorialin examples)
Create vertex types for our tutorial:
CREATE VERTEX TYPE Person
CREATE VERTEX TYPE Movie
CREATE VERTEX TYPE Company
CREATE EDGE TYPE ACTED_IN
CREATE EDGE TYPE DIRECTED
CREATE EDGE TYPE WORKS_AT
CREATE EDGE TYPE KNOWS
Part 1: Creating Data
Creating Nodes
Use the CREATE clause to add nodes (vertices) to your graph.
Create a single node:
CREATE (p:Person {name: 'Alice', age: 30, city: 'New York'})
This creates a node with:
-
Label
Person(the type) -
Properties:
name,age, andcity
Create multiple nodes:
CREATE (a:Person {name: 'Bob', age: 35}),
(b:Person {name: 'Charlie', age: 28}),
(c:Person {name: 'Diana', age: 32})
Creating Relationships
Relationships (edges) connect nodes. Create them with the arrow syntax.
Create a relationship between new nodes:
CREATE (a:Person {name: 'Eve'})-[:KNOWS {since: 2020}]->(b:Person {name: 'Frank'})
Create a relationship between existing nodes:
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2019}]->(b)
Sample Dataset
Let’s create a sample movie database:
// Create people
CREATE (keanu:Person {name: 'Keanu Reeves', born: 1964})
CREATE (carrie:Person {name: 'Carrie-Anne Moss', born: 1967})
CREATE (laurence:Person {name: 'Laurence Fishburne', born: 1961})
CREATE (lana:Person {name: 'Lana Wachowski', born: 1965})
CREATE (lilly:Person {name: 'Lilly Wachowski', born: 1967})
// Create movies
CREATE (matrix:Movie {title: 'The Matrix', released: 1999})
CREATE (reloaded:Movie {title: 'The Matrix Reloaded', released: 2003})
// Create relationships
MATCH (keanu:Person {name: 'Keanu Reeves'}), (matrix:Movie {title: 'The Matrix'})
CREATE (keanu)-[:ACTED_IN {role: 'Neo'}]->(matrix)
MATCH (carrie:Person {name: 'Carrie-Anne Moss'}), (matrix:Movie {title: 'The Matrix'})
CREATE (carrie)-[:ACTED_IN {role: 'Trinity'}]->(matrix)
MATCH (laurence:Person {name: 'Laurence Fishburne'}), (matrix:Movie {title: 'The Matrix'})
CREATE (laurence)-[:ACTED_IN {role: 'Morpheus'}]->(matrix)
MATCH (lana:Person {name: 'Lana Wachowski'}), (matrix:Movie {title: 'The Matrix'})
CREATE (lana)-[:DIRECTED]->(matrix)
MATCH (lilly:Person {name: 'Lilly Wachowski'}), (matrix:Movie {title: 'The Matrix'})
CREATE (lilly)-[:DIRECTED]->(matrix)
Part 2: Reading Data
Basic MATCH Queries
The MATCH clause finds patterns in your graph.
Find all nodes of a type:
MATCH (p:Person)
RETURN p
Find nodes with specific properties:
MATCH (p:Person {name: 'Alice'})
RETURN p
Return specific properties:
MATCH (p:Person)
RETURN p.name, p.age
Filtering with WHERE
Use WHERE for complex conditions:
MATCH (p:Person)
WHERE p.age > 30
RETURN p.name, p.age
Multiple conditions:
MATCH (p:Person)
WHERE p.age >= 25 AND p.age <= 35 AND p.city IS NOT NULL
RETURN p.name, p.age, p.city
String matching:
// Names starting with 'A'
MATCH (p:Person) WHERE p.name STARTS WITH 'A' RETURN p.name
// Names containing 'an'
MATCH (p:Person) WHERE p.name CONTAINS 'an' RETURN p.name
// Email pattern matching
MATCH (p:Person) WHERE p.email =~ '.*@gmail.com' RETURN p.name, p.email
IN operator:
MATCH (p:Person)
WHERE p.name IN ['Alice', 'Bob', 'Charlie']
RETURN p
Traversing Relationships
Find connected nodes by specifying relationship patterns:
Find direct connections:
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, m.title
Find connections with relationship properties:
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
RETURN p.name, r.role, m.title
Find bidirectional connections (either direction):
MATCH (p:Person)-[:KNOWS]-(other:Person)
RETURN p.name, other.name
Multi-hop traversals:
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
WHERE a.name = 'Alice'
RETURN a.name, b.name, c.name
Variable-Length Paths
Find paths of varying lengths:
// Find all people within 1 to 3 hops
MATCH (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b:Person)
RETURN b.name
Named paths:
MATCH p = (a:Person)-[:KNOWS*1..3]->(b:Person)
WHERE a.name = 'Alice'
RETURN p
Part 3: Aggregations
Basic Aggregations
// Count all people
MATCH (p:Person)
RETURN count(p) AS totalPeople
// Count, sum, average
MATCH (p:Person)
RETURN count(p) AS total, sum(p.age) AS totalAge, avg(p.age) AS averageAge
// Min and max
MATCH (p:Person)
RETURN min(p.age) AS youngest, max(p.age) AS oldest
Grouping (Implicit GROUP BY)
Cypher automatically groups by non-aggregated expressions:
// Count people by city
MATCH (p:Person)
RETURN p.city, count(p) AS residents
ORDER BY residents DESC
Multiple grouping keys:
MATCH (p:Person)-[:WORKS_AT]->(c:Company)
RETURN c.name, p.department, count(p) AS employees
ORDER BY c.name, employees DESC
Part 4: Data Modification
Updating with SET
// Update a single property
MATCH (p:Person {name: 'Alice'})
SET p.age = 31
// Update multiple properties
MATCH (p:Person {name: 'Alice'})
SET p.age = 31, p.city = 'Boston'
MERGE (Upsert)
MERGE finds or creates a pattern:
// Create if not exists
MERGE (p:Person {name: 'Grace'})
// With ON CREATE and ON MATCH actions
MERGE (p:Person {name: 'Grace'})
ON CREATE SET p.created = true, p.visits = 1
ON MATCH SET p.visits = p.visits + 1
MERGE relationships:
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
MERGE (a)-[r:KNOWS]->(b)
ON CREATE SET r.since = 2024
ON MATCH SET r.lastContact = 2024
DELETE
Delete a relationship:
MATCH (a:Person {name: 'Alice'})-[r:KNOWS]->(b:Person {name: 'Bob'})
DELETE r
Delete a node (must have no relationships):
MATCH (p:Person {name: 'TestPerson'})
DELETE p
DETACH DELETE (delete node and its relationships):
MATCH (p:Person {name: 'TestPerson'})
DETACH DELETE p
Part 5: Advanced Queries
WITH Clause
Use WITH to chain query parts:
// Filter and project before continuing
MATCH (p:Person)
WITH p.name AS name, p.age AS age
WHERE age > 30
RETURN name ORDER BY name
Aggregation with WITH:
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH m.title AS movie, count(p) AS actorCount
WHERE actorCount > 2
RETURN movie, actorCount
UNWIND
Expand a list into rows:
// Unwind a literal list
UNWIND [1, 2, 3] AS x
RETURN x
// Unwind a property array
MATCH (p:Person {name: 'Alice'})
UNWIND p.hobbies AS hobby
RETURN p.name, hobby
// Create multiple nodes from a list
UNWIND ['Alice', 'Bob', 'Charlie'] AS name
CREATE (p:Person {name: name})
Pattern Predicates
Check if patterns exist:
// Find people who know someone
MATCH (p:Person)
WHERE (p)-[:KNOWS]->()
RETURN p.name
// Find people who don't know anyone
MATCH (p:Person)
WHERE NOT (p)-[:KNOWS]->()
RETURN p.name
// Find people who know a specific person
MATCH (a:Person {name: 'Alice'}), (p:Person)
WHERE (p)-[:KNOWS]->(a)
RETURN p.name AS knowsAlice
Part 6: Using Functions
String Functions
MATCH (p:Person)
RETURN toUpper(p.name) AS upperName,
toLower(p.name) AS lowerName,
substring(p.name, 0, 3) AS initials
Math Functions
MATCH (p:Person)
RETURN p.name,
abs(p.balance) AS absBalance,
round(p.salary / 1000.0) AS salaryK
Part 7: Best Practices
1. Use Labels
Always specify labels in MATCH patterns for better performance:
// Good - uses index on Person
MATCH (p:Person {name: 'Alice'}) RETURN p
// Avoid - scans all nodes
MATCH (n {name: 'Alice'}) RETURN n
2. Create Indexes
Create indexes on frequently queried properties:
CREATE INDEX ON Person (name)
CREATE INDEX ON Movie (title)
3. Use Parameters
For repeated queries, use parameters instead of string concatenation:
MATCH (p:Person)
WHERE p.name = $name AND p.age >= $minAge
RETURN p
4. Limit Results
Use LIMIT for exploratory queries:
MATCH (p:Person)-[:KNOWS*1..5]->(other)
RETURN p.name, other.name
LIMIT 100
5. Use WITH for Complex Queries
Break complex queries into steps with WITH:
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH p, count(m) AS movieCount
WHERE movieCount > 5
MATCH (p)-[:DIRECTED]->(d:Movie)
RETURN p.name, movieCount, collect(d.title) AS directed