Multi-hop patterns and OPTIONAL MATCH

Neo4j · Essentials · 12 min

Real graph questions span several hops, and these patterns are what make Cypher more expressive than joins.

Patterns can be chained to any length: (a)-[:FOLLOWS]->(b)-[:WORKS_AT]->(c:Company) reads "a follows b who works at c". Reusing a variable creates a join point: in MATCH (a)-[:FOLLOWS]->(b), (b)-[:FOLLOWS]->(a) the same a and b must satisfy both patterns, which finds mutual follows.

Within a single MATCH, Cypher never uses the same relationship twice in one match (relationship isomorphism), but the same node can appear more than once. That matters for "friend of a friend" queries, where the start person can show up as their own friend-of-friend; filter with WHERE fof <> me.

OPTIONAL MATCH works like a left outer join: if the pattern is not found, the variables are null instead of the row disappearing.

MATCH (p:Person)
OPTIONAL MATCH (p)-[:WORKS_AT]->(c:Company)
RETURN p.name, c.name

returns every person, with null for those without a company.

Comma-separated patterns that share no variables produce a cartesian product of their matches, which is almost always a mistake and can be huge; Neo4j warns about it.

Example

// recommend people to Ann: followed by people she follows, not yet followed
MATCH (me:Person {name: 'Ann'})-[:FOLLOWS]->(:Person)-[:FOLLOWS]->(fof:Person)
WHERE fof <> me AND NOT (me)-[:FOLLOWS]->(fof)
RETURN fof.name AS suggestion, count(*) AS via
ORDER BY via DESC;

// everyone, with their employer if any
MATCH (p:Person)
OPTIONAL MATCH (p)-[:WORKS_AT]->(c:Company)
RETURN p.name, c.name AS company;
// "Ann" null | "Bob" "Acme" | "Cy" null

Remember

  • Chain hops in one pattern; shared variables join patterns
  • One match never reuses a relationship, but can revisit a node
  • OPTIONAL MATCH yields nulls instead of dropping rows
  • Unconnected comma patterns create cartesian products

Watch out for

  • Forgetting WHERE fof <> me and recommending people to themselves.

Check yourself

  1. What does OPTIONAL MATCH return when its pattern is not found for a row?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: neo4j.com/docs/cypher-manual/current/clauses/optional-match/