Neo4j and Cypher Cheat Sheet
DevOpsDatabase
neo4jcyphergraph-databasecheatsheetreference
Neo4j and Cypher Cheat Sheet
A practical quick-reference guide for Neo4j graph database operations and the Cypher query language. Commands are organized by category with realistic examples.
Creating Data
Create Nodes
-- Create a single node
CREATE (p:Person {name: 'Alice', age: 30})
-- Create multiple nodes in one query
CREATE (p1:Person {name: 'Alice'}),
(p2:Person {name: 'Bob'}),
(p3:Person {name: 'Charlie'})
-- Create node with multiple labels
CREATE (p:Person:Employee {name: 'Alice', department: 'Engineering'})
-- Create node and return it
CREATE (p:Person {name: 'Alice'})
RETURN p
```text
### Create Relationships
```cypher
-- Create a relationship between existing nodes
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS]->(b)
-- Create relationship with properties
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020, strength: 'strong'}]->(b)
-- Create nodes and relationship in one query
CREATE (a:Person {name: 'Alice'})-[:WORKS_FOR {role: 'Engineer'}]->(c:Company {name: 'TechCorp'})
-- Create multiple relationships
MATCH (a:Person {name: 'Alice'})
CREATE (a)-[:LIVES_IN]->(:City {name: 'Seattle'}),
(a)-[:WORKS_FOR]->(:Company {name: 'TechCorp'})
```text
### MERGE (Create If Not Exists)
```cypher
-- Create node only if it does not exist
MERGE (p:Person {name: 'Alice'})
-- MERGE with ON CREATE and ON MATCH
MERGE (p:Person {name: 'Alice'})
ON CREATE SET p.created = timestamp(), p.age = 30
ON MATCH SET p.lastSeen = timestamp()
-- MERGE relationship
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
MERGE (a)-[r:KNOWS]->(b)
ON CREATE SET r.since = 2020
```text
## Matching Patterns
### Basic MATCH
```cypher
-- Find all nodes with a label
MATCH (p:Person)
RETURN p
-- Find nodes with property filter
MATCH (p:Person {name: 'Alice'})
RETURN p
-- Find with multiple conditions
MATCH (p:Person)
WHERE p.age > 25 AND p.city = 'Seattle'
RETURN p.name, p.age
-- Limit results
MATCH (p:Person)
RETURN p
LIMIT 10
```text
### Relationship Patterns
```cypher
-- Outgoing relationship
MATCH (a:Person {name: 'Alice'})-[:KNOWS]->(b)
RETURN b.name
-- Incoming relationship
MATCH (a)<-[:WORKS_FOR]-(e:Person)
RETURN a.name AS company, e.name AS employee
-- Any direction
MATCH (a:Person)-[:KNOWS]-(b:Person)
RETURN a.name, b.name
-- Multiple hops (2 hops away)
MATCH (a:Person {name: 'Alice'})-[:KNOWS*2]->(b)
RETURN DISTINCT b.name
-- Variable length paths (1 to 3 hops)
MATCH (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b)
RETURN DISTINCT b.name
-- Any length path
MATCH (a:Person {name: 'Alice'})-[:KNOWS*]->(b)
RETURN DISTINCT b.name
```text
### Optional Patterns
```cypher
-- LEFT JOIN equivalent (return null if no match)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:WORKS_FOR]->(c:Company)
RETURN p.name, c.name AS company
-- Optional relationship with condition
MATCH (p:Person)
OPTIONAL MATCH (p)-[r:WORKS_FOR]->(c:Company)
WHERE r.role = 'Engineer'
RETURN p.name, c.name
```text
## Updating Data
### SET (Add/Update Properties)
```cypher
-- Update a property
MATCH (p:Person {name: 'Alice'})
SET p.age = 31
-- Add multiple properties
MATCH (p:Person {name: 'Alice'})
SET p.age = 31, p.city = 'Portland', p.updated = timestamp()
-- Replace all properties
MATCH (p:Person {name: 'Alice'})
SET p = {name: 'Alice', age: 31, city: 'Portland'}
-- Add to existing properties
MATCH (p:Person {name: 'Alice'})
SET p += {age: 31, city: 'Portland'}
-- Add label
MATCH (p:Person {name: 'Alice'})
SET p:Employee
```text
### REMOVE (Delete Properties or Labels)
```cypher
-- Remove a property
MATCH (p:Person {name: 'Alice'})
REMOVE p.tempProperty
-- Remove multiple properties
MATCH (p:Person {name: 'Alice'})
REMOVE p.tempProperty, p.draft
-- Remove a label
MATCH (p:Person {name: 'Alice'})
REMOVE p:Employee
-- Remove multiple labels
MATCH (p:Person {name: 'Alice'})
REMOVE p:Employee:Contractor
```text
### DELETE (Delete Nodes and Relationships)
```cypher
-- Delete a node (must have no relationships)
MATCH (p:Person {name: 'Alice'})
DELETE p
-- Delete a relationship
MATCH (a:Person {name: 'Alice'})-[r:KNOWS]->(b:Person {name: 'Bob'})
DELETE r
-- Delete node and all its relationships
MATCH (p:Person {name: 'Alice'})
DETACH DELETE p
-- Delete all nodes of a type (dangerous!)
MATCH (p:Person)
DETACH DELETE p
```text
## Relationships
### Creating Relationships
```cypher
-- Basic relationship
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:FRIEND]->(b)
-- With properties
MATCH (a:Person {name: 'Alice'}), (b:Company {name: 'TechCorp'})
CREATE (a)-[:WORKS_FOR {since: 2020, role: 'Engineer'}]->(b)
-- Multiple relationship types (use | for OR in patterns)
MATCH (a:Person)-[r:KNOWS|FRIEND]->(b:Person)
RETURN type(r), a.name, b.name
```text
### Traversing Relationships
```cypher
-- Outgoing (arrow pointing right)
MATCH (a:Person)-[:KNOWS]->(b)
RETURN a.name, b.name
-- Incoming (arrow pointing left)
MATCH (a:Person)<-[:KNOWS]-(b)
RETURN a.name, b.name
-- Either direction (no arrow head)
MATCH (a:Person)-[:KNOWS]-(b)
RETURN a.name, b.name
-- Named relationship for accessing properties
MATCH (a:Person)-[r:KNOWS]->(b)
RETURN r.since, a.name, b.name
-- Variable length (min 1, max 5 hops)
MATCH (a:Person)-[:KNOWS*1..5]->(b)
RETURN a.name, b.name
-- Exact length (exactly 2 hops)
MATCH (a:Person)-[:KNOWS*2]->(b)
RETURN a.name, b.name
```text
## Filtering
### WHERE Clause
```cypher
-- Basic comparison
MATCH (p:Person)
WHERE p.age > 30
RETURN p.name
-- Multiple conditions
MATCH (p:Person)
WHERE p.age > 25 AND p.city = 'Seattle'
RETURN p.name
-- OR condition
MATCH (p:Person)
WHERE p.city = 'Seattle' OR p.city = 'Portland'
RETURN p.name
-- String matching
MATCH (p:Person)
WHERE p.name STARTS WITH 'A'
RETURN p.name
MATCH (p:Person)
WHERE p.name ENDS WITH 'son'
RETURN p.name
MATCH (p:Person)
WHERE p.name CONTAINS 'ali'
RETURN p.name
-- Regular expression
MATCH (p:Person)
WHERE p.name =~ 'A.*'
RETURN p.name
```text
### IN and Range
```cypher
-- IN list
MATCH (p:Person)
WHERE p.city IN ['Seattle', 'Portland', 'San Francisco']
RETURN p.name
-- Numeric range
MATCH (p:Person)
WHERE p.age >= 25 AND p.age <= 35
RETURN p.name
-- Using IN with numbers
MATCH (p:Person)
WHERE p.age IN [25, 30, 35, 40]
RETURN p.name
```text
### EXISTS and Pattern Matching
```cypher
-- Check if property exists
MATCH (p:Person)
WHERE EXISTS(p.email)
RETURN p.name
-- Check if relationship exists
MATCH (p:Person)
WHERE EXISTS((p)-[:WORKS_FOR]->())
RETURN p.name
-- NOT EXISTS
MATCH (p:Person)
WHERE NOT EXISTS((p)-[:WORKS_FOR]->())
RETURN p.name
-- Pattern in WHERE
MATCH (p:Person)
WHERE (p)-[:LIVES_IN]->(:City {name: 'Seattle'})
RETURN p.name
```text
### NULL Handling
```cypher
-- Check for null
MATCH (p:Person)
WHERE p.email IS NULL
RETURN p.name
-- Check for not null
MATCH (p:Person)
WHERE p.email IS NOT NULL
RETURN p.name
-- Coalesce (return first non-null)
MATCH (p:Person)
RETURN p.name, COALESCE(p.nickname, p.name, 'Unknown') AS displayName
-- Default value with CASE
MATCH (p:Person)
RETURN p.name,
CASE WHEN p.age IS NULL THEN 'Unknown'
ELSE toString(p.age)
END AS age
```text
## Aggregation
### COUNT
```cypher
-- Count all nodes
MATCH (p:Person)
RETURN COUNT(p)
-- Count with grouping
MATCH (p:Person)-[:LIVES_IN]->(c:City)
RETURN c.name, COUNT(p) AS population
-- Count distinct values
MATCH (p:Person)
RETURN COUNT(DISTINCT p.city) AS cities
-- Count relationships
MATCH (p:Person)-[r:KNOWS]->()
RETURN p.name, COUNT(r) AS friends
```text
### SUM, AVG, MIN, MAX
```cypher
-- Sum
MATCH (p:Person)
RETURN SUM(p.salary) AS totalSalary
-- Average
MATCH (p:Person)
RETURN AVG(p.age) AS averageAge
-- Min and Max
MATCH (p:Person)
RETURN MIN(p.age) AS youngest, MAX(p.age) AS oldest
-- With grouping
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN c.name, AVG(p.salary) AS avgSalary, COUNT(p) AS employees
```text
### COLLECT and DISTINCT
```cypher
-- Collect into a list
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN c.name, COLLECT(p.name) AS employees
-- Distinct values
MATCH (p:Person)
RETURN DISTINCT p.city
-- Collect distinct
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN c.name, COLLECT(DISTINCT p.department) AS departments
-- Nested collect
MATCH (c:Company)<-[:WORKS_FOR]-(p:Person)-[:HAS_SKILL]->(s:Skill)
RETURN c.name, COLLECT(DISTINCT {person: p.name, skill: s.name}) AS skills
```text
## Ordering and Pagination
### ORDER BY
```cypher
-- Ascending (default)
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age
-- Descending
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
-- Multiple columns
MATCH (p:Person)
RETURN p.name, p.city, p.age
ORDER BY p.city, p.age DESC
-- Order by aggregated value
MATCH (p:Person)-[:KNOWS]->(f)
RETURN p.name, COUNT(f) AS friendCount
ORDER BY friendCount DESC
```text
### SKIP and LIMIT
```cypher
-- Limit results
MATCH (p:Person)
RETURN p.name
ORDER BY p.name
LIMIT 10
-- Skip results (offset)
MATCH (p:Person)
RETURN p.name
ORDER BY p.name
SKIP 10
-- Pagination (page 2 with 10 per page)
MATCH (p:Person)
RETURN p.name
ORDER BY p.name
SKIP 10 LIMIT 10
```text
## WITH Clause
### Chaining Queries
```cypher
-- Filter after aggregation
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WITH c, COUNT(p) AS employeeCount
WHERE employeeCount > 5
RETURN c.name, employeeCount
-- Transform data between stages
MATCH (p:Person)
WITH p.name AS personName, p.age AS personAge
WHERE personAge > 30
RETURN personName, personAge
-- Multiple aggregations
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WITH c, COLLECT(p.name) AS employees, AVG(p.salary) AS avgSalary
WHERE avgSalary > 50000
RETURN c.name, employees, avgSalary
-- Calculate then filter
MATCH (p:Person)
WITH p, p.salary * 1.1 AS newSalary
WHERE newSalary > 100000
RETURN p.name, newSalary
```text
### Renaming Variables
```cypher
-- Rename for clarity
MATCH (p:Person)-[:WORKS_FOR]->(comp:Company)
WITH p AS employee, comp AS employer
RETURN employee.name, employer.name
-- Preserve only needed variables
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WITH p.name AS name, c.name AS company
RETURN name, company
```text
## Indexes and Constraints
### CREATE INDEX
```cypher
-- Single property index
CREATE INDEX person_name IF NOT EXISTS
FOR (p:Person) ON (p.name)
-- Composite index
CREATE INDEX person_city_age IF NOT EXISTS
FOR (p:Person) ON (p.city, p.age)
-- Full-text index
CREATE FULLTEXT INDEX person_fulltext IF NOT EXISTS
FOR (p:Person) ON EACH [p.name, p.bio]
-- Show indexes
SHOW INDEXES
-- Drop index
DROP INDEX person_name IF EXISTS
```text
### CREATE CONSTRAINT
```cypher
-- Unique constraint
CREATE CONSTRAINT person_email_unique IF NOT EXISTS
FOR (p:Person) REQUIRE p.email IS UNIQUE
-- Existence constraint (property must exist)
CREATE CONSTRAINT person_name_exists IF NOT EXISTS
FOR (p:Person) REQUIRE p.name IS NOT NULL
-- Node key constraint (composite unique + not null)
CREATE CONSTRAINT person_key IF NOT EXISTS
FOR (p:Person) REQUIRE (p.firstName, p.lastName) IS NODE KEY
-- Relationship existence constraint
CREATE CONSTRAINT works_since_exists IF NOT EXISTS
FOR ()-[r:WORKS_FOR]-()
REQUIRE r.since IS NOT NULL
-- Show constraints
SHOW CONSTRAINTS
-- Drop constraint
DROP CONSTRAINT person_email_unique IF EXISTS
```text
## Procedures
### Calling Procedures
```cypher
-- Call built-in procedure
CALL db.labels()
-- Call with arguments
CALL db.schema.visualization()
-- Call APOC procedure (if installed)
CALL apoc.help('search')
-- Call and filter results
CALL db.indexes()
YIELD name, state
WHERE state = 'ONLINE'
RETURN name, state
-- Call with YIELD and WHERE
CALL dbms.security.listUsers()
YIELD username, roles
WHERE 'admin' IN roles
RETURN username
```text
### Listing Available Procedures
```cypher
-- List all procedures
SHOW PROCEDURES
-- List functions
SHOW FUNCTIONS
-- Filter by category
SHOW PROCEDURES YIELD name, category
WHERE category = 'ADMIN'
RETURN name
```text
## Database Management
### Database Operations
```cypher
-- List databases
SHOW DATABASES
-- Show current database
SHOW DEFAULT DATABASE
-- Create database (Enterprise only)
CREATE DATABASE myDatabase IF NOT EXISTS
-- Start database
START DATABASE myDatabase
-- Stop database
STOP DATABASE myDatabase
-- Drop database (Enterprise only)
DROP DATABASE myDatabase IF EXISTS
-- Switch database context
:use myDatabase
```text
### Database Statistics
```cypher
-- Get node count
MATCH (n)
RETURN count(n) AS nodeCount
-- Get counts by label
MATCH (n)
RETURN labels(n) AS labels, count(*) AS count
-- Get relationship count
MATCH ()-[r]->()
RETURN count(r) AS relationshipCount
-- Get counts by relationship type
MATCH ()-[r]->()
RETURN type(r) AS type, count(*) AS count
-- Property existence stats
CALL db.stats.retrieve('GRAPH COUNTS')
YIELD data
RETURN data
```text
## User Management
### User Operations
```cypher
-- Create user
CREATE USER alice SET PASSWORD 'password123' CHANGE NOT REQUIRED
-- Create user with password change required
CREATE USER bob SET PASSWORD 'tempPassword' CHANGE REQUIRED
-- Alter user password
ALTER USER alice SET PASSWORD 'newPassword456'
-- Alter user status
ALTER USER alice SET STATUS SUSPENDED
ALTER USER bob SET STATUS ACTIVE
-- Drop user
DROP USER alice IF EXISTS
-- List users
SHOW USERS
```text
### Role Management
```cypher
-- Create role
CREATE ROLE analyst
-- Grant role to user
GRANT ROLE analyst TO alice
-- Revoke role from user
REVOKE ROLE analyst FROM alice
-- List roles
SHOW ROLES
-- Show user roles
SHOW POPULATED ROLES
```text
### Privileges
```cypher
-- Grant read access
GRANT READ {name, age} ON GRAPH * NODES Person TO analyst
-- Grant traverse (can traverse but not read data)
GRANT TRAVERSE ON GRAPH * TO analyst
-- Grant write access
GRANT WRITE ON GRAPH * TO analyst
-- Grant all privileges
GRANT ALL ON DATABASE * TO admin
-- Revoke privileges
REVOKE READ ON GRAPH * FROM analyst
```text
## Import and Export
### LOAD CSV
```cypher
-- Load CSV with headers
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
CREATE (p:Person {name: row.name, age: toInteger(row.age)})
-- Load CSV without headers
LOAD CSV FROM 'file:///data.csv' AS row
CREATE (p:Person {name: row[0], age: toInteger(row[1])})
-- With field terminator
LOAD CSV WITH HEADERS FROM 'file:///data.tsv' AS row
FIELDTERMINATOR '\t'
CREATE (p:Person {name: row.name})
-- Batch processing with periodic commit
USING PERIODIC COMMIT 1000
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
CREATE (p:Person {name: row.name})
-- Create relationships from CSV
LOAD CSV WITH HEADERS FROM 'file:///works_for.csv' AS row
MATCH (p:Person {name: row.personName})
MATCH (c:Company {name: row.companyName})
CREATE (p)-[:WORKS_FOR {role: row.role}]->(c)
```text
### APOC Load (if APOC is installed)
```cypher
-- Load JSON
CALL apoc.load.json('file:///data.json')
YIELD value
CREATE (p:Person SET p = value)
-- Load JSON from URL
CALL apoc.load.json('https://api.example.com/users')
YIELD value
CREATE (p:Person {name: value.name})
-- Export to JSON
CALL apoc.export.json.query(
'MATCH (p:Person) RETURN p.name',
'people.json',
{stream: true}
)
-- Export to CSV
CALL apoc.export.csv.query(
'MATCH (p:Person) RETURN p.name, p.age',
'people.csv',
{stream: true}
)
```text
## Path Functions
### Shortest Path
```cypher
-- Find shortest path
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
MATCH path = shortestPath((a)-[:KNOWS*]-(b))
RETURN path
-- Shortest path with max length
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CALL apoc.algo.shortestPath(a, b, 'KNOWS')
YIELD path
RETURN path
-- All shortest paths
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
MATCH path = allShortestPaths((a)-[:KNOWS*]-(b))
RETURN path
```text
### Path Operations
```cypher
-- Return path and its length
MATCH path = (a:Person {name: 'Alice'})-[:KNOWS*1..5]-(b)
RETURN path, length(path) AS hops
-- Get nodes from path
MATCH path = (a)-[:KNOWS*]-(b)
RETURN [node IN nodes(path) | node.name] AS names
-- Get relationships from path
MATCH path = (a)-[:KNOWS*]-(b)
RETURN [rel IN relationships(path) | type(rel)] AS relTypes
-- Get start and end nodes
MATCH path = (a)-[:KNOWS*]-(b)
RETURN startNode(path).name AS start, endNode(path).name AS end
```text
## Common Functions
### String Functions
```cypher
-- Concatenation
RETURN toString(42) + ' items' AS result
-- toUpper / toLower
MATCH (p:Person)
RETURN toUpper(p.name) AS nameUpper, toLower(p.name) AS nameLower
-- Substring
RETURN substring('Hello World', 0, 5) AS result -- 'Hello'
-- Trim
RETURN trim(' hello ') AS result -- 'hello'
-- Replace
RETURN replace('hello world', 'world', 'Neo4j') AS result
-- Split
RETURN split('a,b,c', ',') AS result -- ['a', 'b', 'c']
-- Left / Right
RETURN left('Hello', 3) AS result -- 'Hel'
RETURN right('Hello', 3) AS result -- 'llo'
-- String matching
RETURN ltrim(' hello') AS result -- 'hello' (left trim)
RETURN rtrim('hello ') AS result -- 'hello' (right trim)
```text
### Numeric Functions
```cypher
-- Absolute value
RETURN abs(-42) AS result -- 42
-- Rounding
RETURN round(3.7) AS result -- 4
RETURN floor(3.7) AS result -- 3
RETURN ceil(3.2) AS result -- 4
-- Random
RETURN rand() AS random -- 0.0 to 1.0
RETURN toInteger(rand() * 100) AS randomInt -- 0 to 99
-- Sign
RETURN sign(-42) AS result -- -1
RETURN sign(42) AS result -- 1
RETURN sign(0) AS result -- 0
-- Square root
RETURN sqrt(16) AS result -- 4.0
-- Power
RETURN 2^10 AS result -- 1024
RETURN pow(2, 10) AS result -- 1024.0
```text
### List Functions
```cypher
-- Size of list
RETURN size([1, 2, 3, 4, 5]) AS result -- 5
-- Head and last
RETURN head([1, 2, 3]) AS result -- 1
RETURN last([1, 2, 3]) AS result -- 3
-- Range
RETURN range(0, 10) AS result -- [0,1,2,3,4,5,6,7,8,9,10]
RETURN range(0, 10, 2) AS result -- [0,2,4,6,8,10]
-- List comprehension
MATCH (p:Person)
RETURN [x IN p.skills WHERE x <> 'legacy' | toUpper(x)] AS skills
-- Reduce
RETURN reduce(total = 0, x IN [1,2,3,4,5] | total + x) AS sum -- 15
-- Unwind (expand list to rows)
UNWIND [1, 2, 3] AS x
RETURN x * 2 AS doubled
-- Flatten nested lists
RETURN [[1,2], [3,4]] AS nested, flatten([[1,2], [3,4]]) AS flat
```text
### Temporal Functions
```cypher
-- Current date and time
RETURN date() AS today
RETURN datetime() AS now
RETURN time() AS currentTime
RETURN timestamp() AS unixTimestamp
-- Create specific date
RETURN date('2026-02-16') AS specificDate
RETURN date({year: 2026, month: 2, day: 16}) AS specificDate
-- Date arithmetic
MATCH (p:Person)
WHERE date(p.birthDate) > date('2000-01-01')
RETURN p.name
-- Date components
WITH datetime() AS dt
RETURN dt.year, dt.month, dt.day, dt.hour, dt.minute
-- Duration
RETURN duration.between(date('2020-01-01'), date('2026-02-16')) AS diff
RETURN duration.inDays(date('2020-01-01'), date('2026-02-16')).days AS days
```text
### Type Conversion
```cypher
-- To string
RETURN toString(42) AS result
RETURN toString(true) AS result
-- To integer
RETURN toInteger('42') AS result
RETURN toInteger(3.7) AS result -- 3
-- To float
RETURN toFloat('3.14') AS result
-- To boolean
RETURN toBoolean('true') AS result
RETURN toBoolean(1) AS result
-- Check type
RETURN apoc.meta.type(42) AS result -- 'Long'
RETURN apoc.meta.type('hello') AS result -- 'String'
RETURN apoc.meta.type([1,2,3]) AS result -- 'List'
```text
### Coalesce and Null Handling
```cypher
-- COALESCE: return first non-null
RETURN COALESCE(null, null, 'hello') AS result -- 'hello'
-- NullIf: return null if values match
RETURN nullif('hello', 'hello') AS result -- null
RETURN nullif('hello', 'world') AS result -- 'hello'
-- CASE expression
MATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN 'Minor'
WHEN p.age < 65 THEN 'Adult'
ELSE 'Senior'
END AS category
```text
## Graph Data Science (GDS)
The Neo4j Graph Data Science library provides algorithms for analyzing graph structures. Most algorithms support multiple execution modes.
### Setup and Configuration
```cypher
-- Check GDS library version
CALL gds.version()
-- List all available algorithms
CALL gds.list()
-- Check if GDS is properly installed
RETURN gds.version() AS gdsVersion
```text
### Graph Projection
```cypher
-- Project a named graph (native projection)
CALL gds.graph.project(
'myGraph',
'Person',
'KNOWS'
)
-- Project with multiple node labels and relationship types
CALL gds.graph.project(
'socialNetwork',
['Person', 'Company'],
['KNOWS', 'WORKS_FOR']
)
-- Project with relationship properties
CALL gds.graph.project(
'weightedGraph',
'Person',
{
KNOWS: {
properties: ['weight', 'since']
}
}
)
-- Project using Cypher query (Cypher projection)
CALL gds.graph.project.cypher(
'cypherGraph',
'MATCH (p:Person) RETURN id(p) AS id',
'MATCH (p:Person)-[:KNOWS]->(q:Person) RETURN id(p) AS source, id(q) AS target'
)
-- List all projected graphs
CALL gds.graph.list()
-- Get details of a specific graph
CALL gds.graph.list('myGraph')
YIELD graphName, nodeCount, relationshipCount
-- Drop a projected graph
CALL gds.graph.drop('myGraph')
-- Drop graph without error if it does not exist
CALL gds.graph.drop('myGraph', false)
```text
### Centrality Algorithms
Centrality algorithms identify important nodes in a network.
```cypher
-- PageRank: Measures node importance based on incoming relationships
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
-- PageRank with write mode (saves score to database)
CALL gds.pageRank.write('myGraph', {
writeProperty: 'pagerank'
})
-- PageRank with custom damping factor
CALL gds.pageRank.stream('myGraph', {
dampingFactor: 0.85,
maxIterations: 20
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
-- Betweenness Centrality: Measures how often a node lies on shortest paths
CALL gds.betweenness.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
-- Betweenness with sampled approximation (faster for large graphs)
CALL gds.betweenness.stream('myGraph', {
samplingSize: 1000
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
-- Degree Centrality: Counts the number of relationships per node
CALL gds.degree.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
-- Degree centrality for specific relationship direction
CALL gds.degree.stream('myGraph', {
orientation: 'REVERSE'
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
-- Closeness Centrality: Measures average distance to all other nodes
CALL gds.closeness.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
-- Closeness with Wasserman-Faust variant (for disconnected graphs)
CALL gds.closeness.stream('myGraph', {
useWassermanFaust: true
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
```text
### Community Detection
Community detection algorithms find clusters of densely connected nodes.
```cypher
-- Louvain: Detects communities by maximizing modularity
CALL gds.louvain.stream('myGraph')
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY communityId
-- Louvain with write mode
CALL gds.louvain.write('myGraph', {
writeProperty: 'community'
})
-- Louvain with max iterations and hierarchy levels
CALL gds.louvain.stream('myGraph', {
maxIterations: 20,
includeIntermediateCommunities: true
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId
-- Label Propagation: Spreads labels through the network
CALL gds.labelPropagation.stream('myGraph')
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY communityId
-- Label Propagation with seed property
CALL gds.labelPropagation.stream('myGraph', {
seedProperty: 'initialCommunity'
})
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
-- Weakly Connected Components (WCC): Finds disconnected subgraphs
CALL gds.wcc.stream('myGraph')
YIELD nodeId, componentId
RETURN gds.util.asNode(nodeId).name AS name, componentId
ORDER BY componentId
-- WCC with write mode
CALL gds.wcc.write('myGraph', {
writeProperty: 'component'
})
-- Get count of nodes per component
CALL gds.wcc.stream('myGraph')
YIELD componentId
RETURN componentId, count(*) AS componentSize
ORDER BY componentSize DESC
-- Strongly Connected Components (SCC): Mutually reachable nodes
CALL gds.scc.stream('myGraph')
YIELD nodeId, componentId
RETURN gds.util.asNode(nodeId).name AS name, componentId
ORDER BY componentId
-- Triangle Count: Counts triangles each node participates in
CALL gds.triangleCount.stream('myGraph')
YIELD nodeId, triangleCount
RETURN gds.util.asNode(nodeId).name AS name, triangleCount
ORDER BY triangleCount DESC
-- Local Clustering Coefficient: How connected a node's neighbors are
CALL gds.localClusteringCoefficient.stream('myGraph')
YIELD nodeId, localClusteringCoefficient
RETURN gds.util.asNode(nodeId).name AS name, localClusteringCoefficient
ORDER BY localClusteringCoefficient DESC
```text
### Path Finding Algorithms
Path finding algorithms find optimal routes between nodes.
```cypher
-- Dijkstra Shortest Path: Finds shortest path by weight
MATCH (source:Person {name: 'Alice'}), (target:Person {name: 'Bob'})
CALL gds.shortestPath.dijkstra.stream('myGraph', {
sourceNode: source,
targetNode: target,
relationshipWeightProperty: 'weight'
})
YIELD index, sourceNode, targetNode, totalCost, nodeIds, costs, path
RETURN
[nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS nodeNames,
costs,
totalCost
-- Dijkstra with multiple targets
MATCH (source:Person {name: 'Alice'})
CALL gds.shortestPath.dijkstra.stream('myGraph', {
sourceNode: source,
relationshipWeightProperty: 'weight'
})
YIELD nodeIds, totalCost
RETURN totalCost, size(nodeIds) AS pathLength
ORDER BY totalCost
LIMIT 5
-- A* (A-Star) Shortest Path: Uses heuristic for faster pathfinding
MATCH (source:Person {name: 'Alice'}), (target:Person {name: 'Bob'})
CALL gds.shortestPath.astar.stream('myGraph', {
sourceNode: source,
targetNode: target,
relationshipWeightProperty: 'weight',
latitudeProperty: 'lat',
longitudeProperty: 'lon'
})
YIELD nodeIds, totalCost
RETURN [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path, totalCost
-- Yen's K-Shortest Paths: Finds top K shortest paths
MATCH (source:Person {name: 'Alice'}), (target:Person {name: 'Bob'})
CALL gds.allShortestPaths.yens.stream('myGraph', {
sourceNode: source,
targetNode: target,
k: 3,
relationshipWeightProperty: 'weight'
})
YIELD index, nodeIds, totalCost
RETURN index, [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path, totalCost
ORDER BY index
-- Breadth-First Search (BFS): Level-by-level traversal
MATCH (source:Person {name: 'Alice'})
CALL gds.bfs.stream('myGraph', {
sourceNode: source
})
YIELD nodeIds
RETURN [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS visitedNodes
-- BFS with target node
MATCH (source:Person {name: 'Alice'}), (target:Person {name: 'Bob'})
CALL gds.bfs.stream('myGraph', {
sourceNode: source,
targetNodes: [target]
})
YIELD nodeIds
RETURN [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path
-- Depth-First Search (DFS): Explores deeply before backtracking
MATCH (source:Person {name: 'Alice'})
CALL gds.dfs.stream('myGraph', {
sourceNode: source
})
YIELD nodeIds
RETURN [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS visitedNodes
-- All Shortest Paths (unweighted)
MATCH (source:Person {name: 'Alice'}), (target:Person {name: 'Bob'})
CALL gds.allShortestPaths.stream('myGraph', {
sourceNode: source,
targetNode: target
})
YIELD nodeIds
RETURN [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path
```text
### Similarity Algorithms
Similarity algorithms measure how alike nodes are based on their connections.
```cypher
-- Jaccard Similarity: Intersection over union of neighbors
CALL gds.nodeSimilarity.stream('myGraph', {
topK: 5,
similarityCutoff: 0.1
})
YIELD node1, node2, similarity
RETURN
gds.util.asNode(node1).name AS person1,
gds.util.asNode(node2).name AS person2,
similarity
ORDER BY similarity DESC
-- Node Similarity with write mode
CALL gds.nodeSimilarity.write('myGraph', {
writeRelationshipType: 'SIMILAR',
writeProperty: 'score',
topK: 10
})
-- Filter by specific nodes
MATCH (p:Person)
WHERE p.name IN ['Alice', 'Bob', 'Charlie']
WITH collect(p) AS people
CALL gds.nodeSimilarity.stream('myGraph', {
nodeFilter: people
})
YIELD node1, node2, similarity
RETURN gds.util.asNode(node1).name, gds.util.asNode(node2).name, similarity
-- Cosine Similarity: Vector-based similarity
CALL gds.nodeSimilarity.cosine.stream('myGraph', {
nodeProperties: ['feature1', 'feature2', 'feature3']
})
YIELD node1, node2, similarity
RETURN
gds.util.asNode(node1).name AS person1,
gds.util.asNode(node2).name AS person2,
similarity
ORDER BY similarity DESC
-- Pearson Similarity: Correlation-based similarity
CALL gds.nodeSimilarity.pearson.stream('myGraph', {
nodeProperties: ['rating1', 'rating2']
})
YIELD node1, node2, similarity
RETURN
gds.util.asNode(node1).name AS item1,
gds.util.asNode(node2).name AS item2,
similarity
ORDER BY similarity DESC
-- Euclidean Distance: Geometric distance between nodes
CALL gds.alpha.similarity.euclidean.stream({
nodeProperties: 'embedding',
data: [
{item: 'Alice', properties: [1.0, 2.0, 3.0]},
{item: 'Bob', properties: [4.0, 5.0, 6.0]}
]
})
YIELD item1, item2, similarity
RETURN item1, item2, similarity
```text
### Link Prediction Algorithms
Link prediction algorithms estimate the likelihood of future connections.
```cypher
-- Adamic Adar: Weights common neighbors by their degree
MATCH (p1:Person {name: 'Alice'})
MATCH (p2:Person {name: 'Bob'})
RETURN gds.alpha.linkprediction.adamicAdar(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS score
-- Common Neighbors: Counts shared neighbors
MATCH (p1:Person {name: 'Alice'})
MATCH (p2:Person {name: 'Bob'})
RETURN gds.alpha.linkprediction.commonNeighbors(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS commonNeighborCount
-- Preferential Attachment: Product of neighbor counts
MATCH (p1:Person {name: 'Alice'})
MATCH (p2:Person {name: 'Bob'})
RETURN gds.alpha.linkprediction.preferentialAttachment(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS score
-- Resource Allocation: Similar to Adamic Adar with different weighting
MATCH (p1:Person {name: 'Alice'})
MATCH (p2:Person {name: 'Bob'})
RETURN gds.alpha.linkprediction.resourceAllocation(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS score
-- Total Neighbors: Total unique neighbors of both nodes
MATCH (p1:Person {name: 'Alice'})
MATCH (p2:Person {name: 'Bob'})
RETURN gds.alpha.linkprediction.totalNeighbors(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS totalNeighbors
-- Batch link prediction for all unconnected pairs
MATCH (p1:Person), (p2:Person)
WHERE p1.name < p2.name
AND NOT EXISTS((p1)-[:KNOWS]-(p2))
WITH p1, p2,
gds.alpha.linkprediction.adamicAdar(p1, p2, {
relationshipType: 'KNOWS',
direction: 'BOTH'
}) AS score
WHERE score > 0
RETURN p1.name, p2.name, score
ORDER BY score DESC
LIMIT 10
```text
### Node Embeddings
Node embedding algorithms create vector representations of nodes.
```cypher
-- FastRP (Fast Random Projection): Fast embedding generation
CALL gds.fastRP.stream('myGraph', {
embeddingDimension: 128,
randomSeed: 42
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- FastRP with iteration weights
CALL gds.fastRP.stream('myGraph', {
embeddingDimension: 256,
iterationWeights: [0.8, 0.2, 0.02]
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- FastRP with write mode
CALL gds.fastRP.write('myGraph', {
embeddingDimension: 128,
writeProperty: 'embedding'
})
-- FastRP with relationship weight property
CALL gds.fastRP.stream('myGraph', {
embeddingDimension: 128,
relationshipWeightProperty: 'weight'
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- Node2Vec: Embedding based on random walks
CALL gds.node2vec.stream('myGraph', {
embeddingDimension: 128,
walkLength: 80,
walksPerNode: 10,
returnFactor: 1.0,
inOutFactor: 1.0
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- Node2Vec with write mode
CALL gds.node2vec.write('myGraph', {
embeddingDimension: 64,
writeProperty: 'node2vecEmbedding'
})
-- GraphSAGE: Embedding using neural network sampling
CALL gds.beta.graphSage.stream('myGraph', {
embeddingDimension: 128,
sampleSizes: [25, 10],
aggregator: 'mean'
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- GraphSAGE with write mode
CALL gds.beta.graphSage.write('myGraph', {
embeddingDimension: 128,
writeProperty: 'graphSageEmbedding',
modelName: 'myGraphSageModel'
})
-- Train GraphSAGE model for later use
CALL gds.beta.graphSage.train('myGraph', {
embeddingDimension: 128,
modelName: 'myGraphSageModel',
sampleSizes: [25, 10]
})
YIELD modelInfo
RETURN modelInfo
-- HashGNN: Hash-based embedding for large graphs
CALL gds.alpha.hashgnn.stream('myGraph', {
embeddingDimension: 128,
iterations: 5
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
```text
### Graph Projection Modes
GDS algorithms support different execution modes for various use cases.
```cypher
-- STREAM mode: Returns results directly (default for most examples above)
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
-- WRITE mode: Writes results back to the database
CALL gds.pageRank.write('myGraph', {
writeProperty: 'pagerank',
maxIterations: 20
})
YIELD nodePropertiesWritten, ranIterations
RETURN nodePropertiesWritten, ranIterations
-- MUTATE mode: Adds results to the projected graph (not database)
CALL gds.pageRank.mutate('myGraph', {
mutateProperty: 'pagerank'
})
YIELD nodePropertiesWritten, ranIterations
RETURN nodePropertiesWritten, ranIterations
-- STATS mode: Returns only summary statistics
CALL gds.pageRank.stats('myGraph')
YIELD ranIterations, didConverge, preProcessingMillis, computeMillis
-- ESTIMATE mode: Estimates memory and time before running
CALL gds.pageRank.stats.estimate('myGraph', {
maxIterations: 20
})
YIELD bytesMin, bytesMax, nodeCount, relationshipCount
-- Estimate memory for write operation
CALL gds.pageRank.write.estimate('myGraph', {
writeProperty: 'pagerank'
})
YIELD bytesMin, bytesMax, requiredMemory
RETURN requiredMemory
```text
### Pregel API (Custom Algorithms)
```cypher
-- Run a custom Pregel algorithm
CALL gds.alpha.pregel.stream('myGraph', {
maxIterations: 10,
aggregator: 'single',
defaultValue: 0.0
})
YIELD nodeId, values
RETURN gds.util.asNode(nodeId).name AS name, values
-- Pregel with message parsing
CALL gds.alpha.pregel.write('myGraph', {
maxIterations: 20,
writeProperty: 'pregelResult'
})
YIELD nodePropertiesWritten
RETURN nodePropertiesWritten
```text
### Common GDS Patterns
```cypher
-- Run algorithm on filtered subgraph
CALL gds.graph.project.subgraph(
'filteredGraph',
'myGraph',
'n.age > 25',
'*'
)
-- Run multiple algorithms and combine results
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score AS pagerank
WITH nodeId, pagerank
CALL gds.degree.stream('myGraph')
YIELD nodeId AS degreeNodeId, score AS degree
WHERE nodeId = degreeNodeId
RETURN
gds.util.asNode(nodeId).name AS name,
pagerank,
degree
-- Export algorithm results to another graph
CALL gds.pageRank.mutate('myGraph', {
mutateProperty: 'pagerank'
})
YIELD nodePropertiesWritten
WITH 1 AS _
CALL gds.graph.project(
'enrichedGraph',
'*',
'*',
{
nodeProperties: ['pagerank'],
relationshipProperties: []
}
)
RETURN 'Graph created with PageRank' AS result
```text
## Quick Reference
### Node Syntax
```text
(n) -- Any node
(n:Label) -- Node with label
(n:Label {prop: value}) -- Node with label and property
(n:L1:L2) -- Node with multiple labels
```text
### Relationship Syntax
```text
-[r]-> -- Outgoing relationship
-[r:TYPE]-> -- Typed outgoing relationship
-[r:TYPE*]-> -- Variable length
-[r:TYPE*2..5]-> -- Min 2, max 5 hops
```text
### Common Patterns
```cypher
-- Create pattern
CREATE (a)-[:REL]->(b)
-- Match pattern
MATCH (a)-[:REL]->(b)
-- Merge pattern (create if not exists)
MERGE (a)-[:REL]->(b)
-- Return path
MATCH p = (a)-[:REL*]->(b)
RETURN p
```text
## Next Steps
- [CNCF Observability & Analysis](/devops/cncf-observability-analysis/) — Monitoring tools for graph workloads
- [Docker CLI Cheat Sheet](/devops/docker-cheatsheet/) — Containerize Neo4j deployments
- [Kubernetes kubectl Cheat Sheet](/devops/kubernetes-cheatsheet/) — Deploy Neo4j on Kubernetes