Cypher#
Cypher is the query language for graph databases. Created at Neo4j; later open-sourced as openCypher and adopted by other graph engines (Memgraph, Amazon Neptune via openCypher, RedisGraph). The “SQL of the graph world”.
The defining feature: queries look like the data they’re querying. ASCII art of nodes and edges drives the syntax.
The ASCII-Art Graph#
In Cypher, nodes are (parens) and edges are -[brackets]->:
(alice)-[:FRIEND_OF]->(bob)
A pattern that says: match anywhere there’s a FRIEND_OF edge from a node we’ll call alice to a node we’ll call bob.
Reading the graph as a sentence is half the win.
The Basics#
The everyday Cypher operations cover finding by property, walking a relationship, traversing N hops, and finding the shortest path between two nodes. Each one packs a query that would take a join-heavy SQL stanza into a single readable line of pattern syntax.
// Find a person named Ada
MATCH (p:Person {name: 'Ada'})
RETURN p
// Friends of Ada
MATCH (a:Person {name: 'Ada'})-[:FRIEND_OF]->(f)
RETURN f.name
// Friends of friends, distinct
MATCH (a:Person {name: 'Ada'})-[:FRIEND_OF*2]->(fof)
WHERE fof <> a
RETURN DISTINCT fof.name
// Shortest path
MATCH p = shortestPath(
(a:Person {name: 'Ada'})-[*..6]-(b:Person {name: 'Lin'})
)
RETURN p
Clauses#
The verbs of Cypher. MATCH reads, CREATE /
MERGE writes, WITH chains stages, RETURN projects.
Most of the table will look familiar to a SQL author –
ORDER BY, LIMIT, UNION, CALL, with
graph-shaped semantics underneath.
Clause |
Purpose |
|---|---|
|
pattern match against the graph |
|
|
|
filter |
|
project result |
|
chain query stages (like a CTE) |
|
turn a list into rows |
|
create nodes / relationships |
|
match-or-create (upsert) |
|
update properties |
|
|
|
|
|
|
|
|
|
SQL-style result shaping |
|
combine results |
|
invoke procedures |
Pattern Syntax#
The patterns are the language. Parentheses describe nodes, square brackets describe edges, dashes connect them, arrows add direction. Labels and properties narrow the matches; star notation expresses variable-length traversal (two to five hops, any number, exactly four).
Pattern |
Matches |
|---|---|
|
any node, bind to |
|
node with label |
|
|
|
directed edge a → b |
|
directed edge a ← b |
|
either direction |
|
named edge |
|
edge type |
|
2 to 5 hops |
|
any number of hops |
Building Up Queries with WITH#
WITH is Cypher’s CTE: it pipes results from one stage to the next.
MATCH (a:Person)-[:FRIEND_OF]->(b)
WITH a, count(b) AS friend_count
WHERE friend_count > 5
RETURN a.name, friend_count
ORDER BY friend_count DESC
LIMIT 10
Writing Data#
Cypher’s mutating verbs. CREATE adds; MERGE upserts
with optional ON CREATE / ON MATCH branches; SET
updates properties or adds labels; DETACH DELETE removes
a node and all its edges in one shot. The patterns mirror
the read syntax so writes look like a planned subgraph edit.
// Create
CREATE (p:Person {name: 'Ada', age: 36})
CREATE (a)-[:FRIEND_OF {since: 2020}]->(b)
// Upsert
MERGE (p:Person {email: 'operator@example.com'})
ON CREATE SET p.created_at = timestamp()
ON MATCH SET p.last_seen = timestamp()
// Update
MATCH (p:Person {name: 'Ada'})
SET p.age = 37, p:Active
// Remove
MATCH (p:Person {name: 'Ada'})
DETACH DELETE p // delete the node and all its edges
Aggregations#
MATCH (p:Person)-[:WROTE]->(post:Post)
RETURN p.name, count(post) AS posts, avg(post.length) AS avg_len
Where Cypher Wins#
Cypher is dramatically clearer than SQL for queries with deep traversal:
“Find everyone within 4 hops of Ada whose company is in France” – one Cypher pattern; a join-heavy nightmare in SQL.
“Detect cycles among accounts”, Cypher’s variable-length paths handle it naturally.
“Recommend products bought by people who bought what I bought” – graph projection with two MATCH clauses.
For tabular relational queries, SQL is still the right tool. Cypher shines exactly where SQL struggles.
Implementations#
The graph databases that speak Cypher. Neo4j is the reference; Memgraph is the in-memory openCypher rebuild; Amazon Neptune supports openCypher alongside Gremlin and SPARQL; Apache AGE bolts Cypher onto Postgres. The dialect is portable enough that queries move between engines.
Neo4j, the original; the reference Cypher.
Memgraph, in-memory, openCypher-compatible, C++.
Amazon Neptune, supports openCypher (alongside Gremlin and SPARQL).
RedisGraph, now EOL but spread Cypher into the Redis ecosystem.
Apache AGE, adds graph queries (openCypher) on top of PostgreSQL.
Other Graph Query Languages#
Cypher isn’t the only way to query a graph. Gremlin is the imperative traversal alternative; SPARQL queries RDF triples; GQL is the ISO-standardized successor. Picking openCypher in 2026 is the safest syntactic choice, and GQL ratifies it as the going-forward standard.
Gremlin, imperative graph traversal language; part of Apache TinkerPop. Different mental model: chained traversal steps rather than pattern matching. Strong in JanusGraph and Neptune.
SPARQL, query language for RDF / linked data. Pattern-based like Cypher but for triples (subject-predicate-object).
GQL (Graph Query Language), ISO standard adopted in 2024, built on Cypher’s foundations; long-term unification target.
If you’re picking a graph stack today, openCypher is the safest syntactic bet, and GQL ratifies it as the standard going forward.
Pitfalls#
The traps that catch teams new to Cypher. Missing indexes turn fast queries slow; disjoint MATCH patterns produce Cartesian explosions; uncapped variable-length paths exhaust memory; the schema-less default lets bad data accumulate. Each is a known issue with a one-line preventive fix.
Performance is sensitive to label and index choices. Always index the property you
MATCHon withCREATE INDEX.Cartesian products,
MATCH (a), (b)without a relationship produces every pair. Avoid disjoint patterns; useWITHto chain.Variable-length paths without an upper bound (
[*]) can explode. Always cap them:[*..6].Schema-less, Neo4j enforces only what you tell it via constraints; bad data goes in and silently breaks queries later. Define
CREATE CONSTRAINTfor keys and existence.
When (Not) to Use Cypher#
Use for relationship-heavy queries: social graphs, fraud detection, identity / access analyses, recommendations, dependency / impact analysis.
Don’t use for primary OLTP storage of tabular records, a relational database is usually a better fit.
Don’t use purely for “joins are slow” reasons, a well-indexed Postgres on millions of rows is often faster than a graph database.