Skip to content
Part IA Michaelmas Term

Pattern Matching in Cypher

Pattern matching syntax

Cypher’s central operation is pattern matching. A MATCH clause describes a subgraph pattern, and the query engine finds all occurrences in the stored graph. Patterns use ASCII-art syntax to represent nodes and edges.

Basic patterns

PatternMeaning
(a)-->(b)Any directed edge from a to b
(a:Person)-->Any edge from a node of type Person
(b:Movie)Any node of type Movie
(a)-[:ACTED_IN]->(b)An ACTED_IN edge from a to b
()-->(a)<--()Nodes with two or more incoming edges
(a)--(b)Edges in either direction (undirected match)

The (a)-->(b) form is the simplest: it matches any node connected to any other node by a single directed edge. The (a:Person)-->(b:Movie) form adds type constraints.

Variable-length paths (Kleene star)

Cypher supports transitive matching using the Kleene star (zero or more steps):

(a:Person)-[:ACTED_IN*]-(b)

This matches paths of any length where every edge has type ACTED_IN. The * operator is analogous to the Kleene closure operation from formal languages: it matches zero, one, or more repetitions of the edge pattern.

Bounded paths use the [*min..max] syntax:

(a)-[*3..5]->(b)

This matches paths of length 3 to 5 (inclusive). Useful bounds prevent runaway search on large graphs.

Shortest path qualification:

(a)-[:ACTED_IN*..6]-(b)

This matches paths of length up to 6 (with *..6 as shorthand for *0..6).

Friend-of-a-friend example

MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof)
RETURN john.name, fof.name

This query finds all nodes fof reachable from John by exactly two FRIEND edges. The empty () in the middle matches any intermediate node — it is a wildcard for the friend of John who is also the friend of fof.

The result set contains one row per unique (john, fof) pair that is connected by a two-step path. If there are multiple mutual friends connecting John to the same fof, that fof appears only once (because MATCH returns distinct pattern matches, not every path).

Co-actors query

Actors who have appeared in the same film as each other:

MATCH (p1:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(p2:Person)
WHERE p1.person_id <> p2.person_id
RETURN p1.name, p2.name, count(*) AS total
ORDER BY total DESC
LIMIT 10

This pattern matches a pair of people both linked by ACTED_IN edges to the same movie node. The WHERE clause excludes self-pairs (the same person matched twice).

Equivalent formulations of the same query

The co-actors query can be written in several equivalent ways:

Form 1 — Two separate patterns (conjunction):

MATCH (p1:Person)-[:ACTED_IN]->(m:Movie),
      (p2:Person)-[:ACTED_IN]->(m:Movie)
WHERE p1.person_id <> p2.person_id

Form 2 — Chained pattern (as above):

MATCH (p1:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(p2:Person)
WHERE p1.person_id <> p2.person_id

Form 3 — Two-hop transitive match:

MATCH (p1:Person)-[:ACTED_IN*2]-(p2:Person)
WHERE p1.person_id <> p2.person_id

Form 3 is semantically different: it matches any two-step path where both edges are ACTED_IN, but it does not require that both edges point to the same movie. An actor could be connected to another actor by acting in Movie A and the second actor acting in Movie B — this would also be a two-hop path, but they are not co-stars. Form 3 overcounts.

Return and aggregation

RETURN can include aggregate functions (count, sum, avg, collect). When aggregation is present, non-aggregated columns form implicit grouping keys — the same semantics as SQL’s GROUP BY on all non-aggregated columns.

Summary

  • Cypher patterns use (node)-[edge]->(node) ASCII-art syntax to describe subgraph structures.
  • Type constraints use colon syntax: (a:Person), [:ACTED_IN].
  • The Kleene star * enables transitive matching; bounds are written as [*min..max].
  • The same graph query can often be expressed in multiple ways with different semantics.
  • Two-hop paths via the same node require an intermediate variable, not just *2.