Neo4j (Graph Database) Cheat Sheet
Cypher query language essentials for creating nodes and relationships, traversing graphs, and finding shortest paths in Neo4j.
Creating Nodes & Relationships
Basic graph creation in Cypher.
// Create nodesCREATE (a:Person {name: "Alice", age: 30})CREATE (b:Person {name: "Bob", age: 25})// Create a relationship between existing nodesMATCH (a:Person {name: "Alice"}), (b:Person {name: "Bob"})CREATE (a)-[:FRIENDS_WITH {since: 2020}]->(b)
Querying & Traversal
Matching patterns and finding paths.
// Find a nodeMATCH (p:Person {name: "Alice"}) RETURN p// Traverse a relationshipMATCH (a:Person)-[:FRIENDS_WITH]->(b:Person)RETURN a.name, b.name// Shortest path between two nodesMATCH path = shortestPath( (a:Person {name: "Alice"})-[*]-(b:Person {name: "Bob"}))RETURN path
Update, Delete & Merge
Modifying the graph safely.
MATCH (p:Person {name: "Alice"})SET p.age = 31MATCH (p:Person {name: "Bob"})DETACH DELETE p // deletes the node and its relationshipsMERGE (p:Person {name: "Carol"})ON CREATE SET p.createdAt = timestamp()
Core Concepts
Terminology behind Neo4j's property graph model.
- Node- an entity with a label (e.g. :Person) and key-value properties
- Relationship- a directed, typed connection between two nodes; can hold properties
- Label- a tag categorizing a node; a node may have multiple labels
- MATCH- the Cypher clause for pattern matching against the graph
- MERGE- "find or create": matches a pattern or creates it if absent
- CREATE CONSTRAINT ... IS UNIQUE- enforces uniqueness and backs it with an index
Variable-Length Paths & Path Filtering
Traverse a bounded number of hops and filter on relationship types along the way.
// Friends-of-friends up to 3 hops, only through FRIENDS_WITH edgesMATCH (a:Person {name: "Alice"})-[:FRIENDS_WITH*1..3]-(fof:Person)WHERE a <> fofRETURN DISTINCT fof.name// All shortest paths (not just one) between two nodesMATCH p = allShortestPaths((a:Person {name: "Alice"})-[:FRIENDS_WITH*]-(b:Person {name: "Zoe"}))RETURN p// Weighted path cost using reduce()MATCH p = (a:City {name:"NYC"})-[:ROAD*1..5]->(b:City {name:"Boston"})RETURN p, reduce(cost = 0, r IN relationships(p) | cost + r.distance) AS totalDistanceORDER BY totalDistance ASC LIMIT 1
Aggregation, collect() & Pattern Comprehension
Group results and build nested collections without leaving Cypher.
// Group friends per person into a listMATCH (p:Person)-[:FRIENDS_WITH]->(f:Person)RETURN p.name, collect(f.name) AS friends, count(f) AS friendCountORDER BY friendCount DESC// Pattern comprehension: inline sub-query producing a listMATCH (p:Person)RETURN p.name, [(p)-[:FRIENDS_WITH]->(f) WHERE f.age > 21 | f.name] AS adultFriends// UNWIND to expand a list back into rows for bulk writesUNWIND [{name:"Dan", age:40}, {name:"Eve", age:29}] AS rowCREATE (:Person {name: row.name, age: row.age})
APOC: Procedures Beyond Core Cypher
Common APOC calls for periodic batching and JSON/graph utilities.
// Batch a large write over 10k-row chunks (avoids one giant transaction)CALL apoc.periodic.iterate( "MATCH (p:Person) WHERE p.migrated IS NULL RETURN p", "SET p.migrated = true, p.score = coalesce(p.score, 0) + 1", { batchSize: 10000, parallel: true })// Export a subgraph to JSONCALL apoc.export.json.query( "MATCH (p:Person)-[r:FRIENDS_WITH]->(f) RETURN p, r, f", "friends.json", {})// Load and merge nodes from an external JSON/CSV endpointCALL apoc.load.json("https://api.example.com/people") YIELD valueMERGE (p:Person {id: value.id}) SET p += value
Constraints & Composite/Full-Text Indexes
Schema enforcement and search acceleration beyond a single uniqueness rule.
// Uniqueness + existence constraintsCREATE CONSTRAINT person_email_unique IF NOT EXISTSFOR (p:Person) REQUIRE p.email IS UNIQUE;CREATE CONSTRAINT person_name_exists IF NOT EXISTSFOR (p:Person) REQUIRE p.name IS NOT NULL;// Composite index across two propertiesCREATE INDEX person_name_age IF NOT EXISTSFOR (p:Person) ON (p.name, p.age);// Full-text index for fuzzy/relevance searchCREATE FULLTEXT INDEX personSearch IF NOT EXISTSFOR (p:Person) ON EACH [p.name, p.bio];CALL db.index.fulltext.queryNodes("personSearch", "alise~") YIELD node, scoreRETURN node.name, score ORDER BY score DESC
Query Planning & Tuning Vocabulary
Terms you need to read EXPLAIN/PROFILE output and reason about performance.
- PROFILE- runs the query and reports actual db hits per operator, the ground truth for optimization
- EXPLAIN- shows the planned execution without running it, useful for a quick sanity check
- NodeByLabelScan- a full label scan; a red flag on large label sets if it should have used an index seek
- Cartesian product- happens when two disconnected MATCH patterns are combined; watch for it in PROFILE, it's usually unintentional
- Eager operator- forces the whole intermediate result to materialize before continuing, often triggered by mixing MERGE/DELETE with MATCH
- db hits- the unit PROFILE uses to count storage-engine operations; lower is better and comparable across query rewrites
- Index seek vs scan- a seek uses an index to jump directly to matching nodes; a scan walks every node with the label
Use MERGE only on the smallest pattern that must be unique, then attach ON CREATE SET / ON MATCH SET for the rest — merging on a large multi-property pattern can silently create duplicate nodes when any single property fails to match.