Cypher Quick Reference Guide
A comprehensive quick reference for Cypher, the query language for graph databases. This guide focuses on Cypher syntax and patterns that work across Neo4j, Kuzu, and other graph databases supporting Cypher.
A comprehensive quick reference for Cypher, the query language for graph databases. This guide focuses on Cypher syntax and patterns that work across Neo4j, Kuzu, and other graph databases supporting Cypher.
If you’re coming from a SQL background, you’ll find Cypher surprisingly familiar. Both are declarative query languages, but they’re optimized for different data models:
Key Similarities:
-
MATCH is like SELECT – it retrieves data
-
WHERE works exactly the same for filtering
-
RETURN is like SELECT in SQL – it projects what to return
-
CREATE is like INSERT – adds new data
-
DELETE is like DELETE – removes data
-
Aggregations (count, sum, avg) work similarly
-
WITH is like subqueries or CTEs (Common Table Expressions)
Key Differences:
-
Instead of tables and rows, you work with nodes and relationships
-
Pattern matching replaces JOINs: (a)-[:REL]->(b) instead of FROM table1 JOIN table2
-
Relationships are first-class citizens with types and directions
-
No need for foreign keys – relationships are explicit
Think of Cypher as SQL for graphs, where you describe the shape of the data you want rather than how to join tables.
-
Nodes: Entities (vertices) with labels and properties
-
Relationships: Connections between nodes with types and properties
-
Properties: Key-value pairs on nodes and relationships
-
MATCH, CREATE, MERGE, WHERE, RETURN, WITH are case-insensitive
-
Strings: ‘single quotes’ or “double quotes”
-
Comments: // single line or /_ multi-line _/
-
Variables: case-sensitive, start with letter or underscore
// Basic node pattern(n) // Any node(n:Person) // Node with label Person(n:Person {name: 'Alice'}) // Node with label and properties(p:Person:Employee) // Node with multiple labels// Directed relationships()-[]->() // Any relationship, directed()-[:KNOWS]->() // Specific relationship type()-[:KNOWS|FRIENDS]->() // Multiple relationship types()-[:KNOWS*1..3]->() // Variable length (1 to 3 hops)
// Undirected relationships()-[]-() // Any relationship, undirected()-[:KNOWS]-() // Specific type, undirected
// Relationship with properties()-[:KNOWS {since: 2020}]->()()-[r:KNOWS]->() // Capture relationship as variablePurpose: Creates new nodes and/or relationships in the graph. Unlike MERGE, CREATE always creates new elements, even if they already exist.
// Create a simple nodeCREATE (n:Person {name: 'Alice', age: 30})
// Create multiple nodesCREATE (p1:Person {name: 'Alice'}), (p2:Person {name: 'Bob'})
// Create nodes and relationshipCREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}), (a)-[:KNOWS {since: 2020}]->(b)
// Create with pathCREATE p = (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})RETURN pPurpose: Finds and matches patterns in the graph. It’s the most common clause for querying data.
// Match all nodesMATCH (n) RETURN n
// Match specific labelMATCH (p:Person) RETURN p.name, p.age
// Match with relationshipMATCH (a:Person)-[:KNOWS]->(b:Person)RETURN a.name, b.name
// Match with variable lengthMATCH (a:Person)-[:KNOWS*1..2]->(b:Person)RETURN a.name, b.name
// Match with relationship variableMATCH (a)-[r:KNOWS]->(b)RETURN type(r), properties(r)
// Optional matchOPTIONAL MATCH (p:Person)-[:HAS_PET]->(pet)RETURN p.name, pet.namePurpose: Ensures existence – creates if not found, matches if exists. Like CREATE + MATCH combined. Prevents duplicates.
// MERGE ensures existence (like CREATE or MATCH)MERGE (p:Person {name: 'Alice'})ON CREATE SET p.age = 30, p.created = timestamp()ON MATCH SET p.lastSeen = timestamp()
// MERGE with relationshipsMERGE (a:Person {name: 'Alice'})MERGE (b:Person {name: 'Bob'})MERGE (a)-[:KNOWS]->(b)
// MERGE with pathMERGE p = (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})ON CREATE SET p.created = timestamp()Purpose: Filters results based on conditions. Works with MATCH, CALL, and other clauses.
// Basic filteringMATCH (p:Person)WHERE p.age > 25RETURN p.name
// Multiple conditionsMATCH (p:Person)WHERE p.age > 25 AND p.city = 'New York'RETURN p.name
// String operationsMATCH (p:Person)WHERE p.name STARTS WITH 'A'RETURN p.name
// Pattern in WHEREMATCH (p:Person)WHERE EXISTS { MATCH (p)-[:HAS_FRIEND]->(friend) WHERE friend.age > 30}RETURN p.name
// Comparison with listMATCH (p:Person)WHERE p.age IN [25, 30, 35]RETURN p.namePurpose: Specifies what data to return from the query. Projects and formats the final result.
// Basic returnMATCH (p:Person)RETURN p.name, p.age
// Return allMATCH (p:Person)RETURN p
// Return with aliasMATCH (p:Person)RETURN p.name AS fullName, p.age AS years
// Return with calculationsMATCH (p:Person)RETURN p.name, p.age + 5 AS futureAge
// Return distinctMATCH (p:Person)-[:KNOWS]->(friend)RETURN DISTINCT p.name, friend.name
// Limit resultsMATCH (p:Person)RETURN p.nameLIMIT 10
// Order resultsMATCH (p:Person)RETURN p.name, p.ageORDER BY p.age DESCPurpose: Passes results from one part of the query to the next. Enables aggregation and chaining of complex queries.
// Pass results to next stepMATCH (p:Person)-[:KNOWS]->(friend)WITH p, count(friend) AS friendCountWHERE friendCount > 5RETURN p.name, friendCount
// Aggregate and continueMATCH (p:Person)-[:HAS_ORDER]->(o:Order)WITH p, sum(o.amount) AS totalSpentWHERE totalSpent > 1000MATCH (p)-[:HAS_FRIEND]->(friend)RETURN p.name, totalSpent, count(friend) AS friends
// Multiple WITH clausesMATCH (p:Person)-[:HAS_POST]->(post:Post)WITH p, count(post) AS postCountWITH p, postCount, postCount * 2 AS doubledWHERE doubled > 10RETURN p.name, postCount, doubledPurpose: Captures and manipulates entire paths (sequences of nodes and relationships) for complex traversals.
// Capture entire pathMATCH p = (a:Person)-[:KNOWS*1..3]->(b:Person)RETURN p
// Path functionsMATCH p = (a:Person)-[:KNOWS]->(b:Person)RETURN nodes(p), relationships(p)
// Shortest pathMATCH p = shortestPath((a:Person)-[:KNOWS*]-(b:Person))WHERE a.name = 'Alice' AND b.name = 'Bob'RETURN p
// All shortest pathsMATCH p = allShortestPaths((a:Person)-[:KNOWS*]-(b:Person))WHERE a.name = 'Alice' AND b.name = 'Bob'RETURN p
// Path lengthMATCH p = (a:Person)-[:KNOWS*]->(b:Person)WHERE length(p) > 2RETURN a.name, b.name, length(p) AS pathLength
// Extract from pathMATCH p = (a:Person)-[:KNOWS]->(b:Person)-[:WORKS_AT]->(c:Company)RETURN a.name, b.name, c.name, [node in nodes(p) | node.name] AS nodeNamesPurpose: Executes procedures (predefined functions) to perform operations beyond standard Cypher. Used for system functions, data import, algorithms, etc.
// CALL with built-in proceduresCALL db.info() YIELD version, editionRETURN version, edition
// CALL with parametersCALL db.awaitIndex(':Person(name)', 30) YIELD valueRETURN value
// CALL with subquery (Neo4j 4.x+)CALL { MATCH (p:Person) WHERE p.age > 30 RETURN count(p) AS olderThan30}RETURN olderThan30
// CALL with apoc procedures (Neo4j only)CALL apoc.load.json('https://api.example.com/data')YIELD valueCREATE (n:Node {data: value})
// CALL with aggregationMATCH (p:Person)CALL { WITH p MATCH (p)-[:KNOWS]->(friend) RETURN count(friend) AS friendCount}RETURN p.name, friendCount
// CALL with YIELD and WHERECALL db.propertyKeys() YIELD propertyKeyWHERE propertyKey STARTS WITH 'name'RETURN propertyKey
// CALL with multiple YIELDCALL db.labels() YIELD labelWITH labelCALL db.indexes() YIELD indexName, propertiesWHERE label IN propertiesRETURN label, indexName, propertiesPurpose: Removes nodes and relationships from the graph. DELETE removes only if no relationships exist, DETACH removes relationships along with nodes.
// Delete nodes (only if no relationships exist)MATCH (p:Person {name: 'Alice'})DELETE p
// Detach delete nodes (removes relationships too)MATCH (p:Person {name: 'Alice'})DETACH DELETE p
// Delete specific relationshipsMATCH (a:Person)-[r:KNOWS]->(b:Person)WHERE a.name = 'Alice' AND b.name = 'Bob'DELETE r
// Detach delete all relationships of a nodeMATCH (p:Person {name: 'Alice'})-[r]-()DELETE r
// Delete with patternMATCH (p:Person {name: 'Alice'})-[r:KNOWS]-()DETACH DELETE p, r
// Remove properties (not delete)MATCH (p:Person {name: 'Alice'})REMOVE p.tempProperty
// Remove labelsMATCH (p:Person {name: 'Alice'})REMOVE p:Person// CountMATCH (p:Person)RETURN count(p) AS totalPeople
// Count distinctMATCH (p:Person)-[:KNOWS]->(friend)RETURN p.name, count(DISTINCT friend) AS uniqueFriends
// Sum, avg, min, maxMATCH (p:Person)RETURN sum(p.age) AS totalAge, avg(p.age) AS averageAge, min(p.age) AS youngest, max(p.age) AS oldest
// CollectMATCH (p:Person)-[:KNOWS]->(friend)RETURN p.name, collect(friend.name) AS friends
// Aggregation with conditionsMATCH (p:Person)RETURN count(CASE WHEN p.age = 30 THEN 1 END) AS mature// Create indexCREATE INDEX person_name_index FOR (p:Person) ON (p.name)
// Create unique constraintCREATE CONSTRAINT person_name_unique FOR (p:Person) REQUIRE p.name IS UNIQUE
// Create node key constraintCREATE CONSTRAINT person_node_key FOR (p:Person) REQUIRE (p.name, p.email) IS NODE KEY
// Show indexesSHOW INDEXES
// Show constraintsSHOW CONSTRAINTS
// Drop indexDROP INDEX person_name_index
// Drop constraintDROP CONSTRAINT person_name_unique// Find mutual friendsMATCH (a:Person)-[:KNOWS]->(mutual:Person)(f))
// Using CALL with aggregationMATCH (p:Person)CALL { WITH p MATCH (p)-[:KNOWS]->(friend) RETURN count(friend) AS friendCount}RETURN p.name, friendCount
// CALL with apoc procedures (Neo4j only)CALL apoc.load.json('https://api.example.com/data')YIELD valueCREATE (n:Node {data: value})
// CALL with db proceduresCALL db.propertyKeys() YIELD propertyKeyRETURN collect(propertyKey) AS allProperties-
Embedded: No server required, runs in-process
-
Performance: Optimized for analytical queries
-
Simplicity: Easier setup and deployment
-
Memory efficient: Better for large graphs on limited resources
-
APOC Library: Extensive procedures for data import, graph algorithms, etc.
-
Bloom: Visual query builder
-
Enterprise features: Clustering, security, monitoring
-
Larger ecosystem: More tools and integrations
Kuzu doesn’t have APOC, but provides:
-
Built-in functions for common operations
-
Python/C++ API for custom extensions
-
Regular updates with new features
-
Most basic Cypher works in both
-
APOC procedures need custom implementation in Kuzu
-
Kuzu focuses on analytical queries
-
Neo4j better for transactional workloads
-
Always use labels for better performance
-
Use parameters in applications: CREATE (p:Person {name: $name})
-
Profile queries: PROFILE MATCH… to optimize
-
Use indexes for frequently queried properties
-
MERGE over CREATE when you want to avoid duplicates
-
WITH for chaining complex queries
-
Path variables for complex traversals
-
Cypher Reference Manual
-
Kuzu Documentation
-
Cypher Cheatsheet