Advanced Pattern Matching Reference

This reference documents advanced GQL pattern matching capabilities including variable-length paths, quantified patterns, path expressions, and complex graph traversals.

Pattern Syntax Overview

Basic Pattern Elements

<pattern> ::=
    <node-pattern>
  | <node-pattern> <relationship-pattern> <node-pattern>
  | <path-pattern>

<node-pattern> ::=
    '(' [<variable>] [<label-expression>] [<properties>] ')'

<relationship-pattern> ::=
    <left-arrow>? '-' [<relationship-detail>] '-' <right-arrow>?

<relationship-detail> ::=
    '[' [<variable>] [<type-expression>] [<quantifier>] [<properties>] ']'

Node Patterns

Basic Node Pattern

-- Anonymous node
MATCH (n)
RETURN n;

-- Named node
MATCH (person)
RETURN person;

-- With label
MATCH (p:Person)
RETURN p;

-- Multiple labels
MATCH (e:Person:Employee)
RETURN e;

-- With properties
MATCH (p:Person {name: 'Alice', age: 30})
RETURN p;

Label Expressions

-- Single label
MATCH (n:Person)

-- Multiple labels (AND - node must have all)
MATCH (n:Person:Employee)

-- Label disjunction (OR - node must have at least one)
MATCH (n:Person|Company)

-- Label negation
MATCH (n:!Deleted)

-- Complex label expressions
MATCH (n:(Person|Company)&!Archived)

Property Predicates in Pattern

-- Equality
MATCH (p:Person {name: 'Alice'})

-- Multiple properties
MATCH (p:Person {name: 'Alice', active: true})

-- With parameter
MATCH (p:Person {email: $email})

-- NULL property (use WHERE instead)
MATCH (p:Person)
WHERE p.phone IS NULL

Relationship Patterns

Direction

-- Outgoing
MATCH (a)-[r]->(b)

-- Incoming
MATCH (a)<-[r]-(b)

-- Undirected (either direction)
MATCH (a)-[r]-(b)

Relationship Types

-- Single type
MATCH (a)-[:KNOWS]->(b)

-- Multiple types (OR)
MATCH (a)-[:KNOWS|FRIENDS_WITH]->(b)

-- Any type (omit type specification)
MATCH (a)-[r]->(b)

Relationship Properties

-- With properties
MATCH (a)-[r:KNOWS {since: 2020}]->(b)

-- Named with properties
MATCH (a)-[friendship:FRIENDS_WITH {since: $year}]->(b)
RETURN friendship.trust_level;

Variable-Length Paths

Syntax

<quantifier> ::=
    '*'                    -- Zero or more (Kleene star)
  | '*' <min>              -- At least min
  | '*' <min> '..'         -- At least min (same as above)
  | '*' '..' <max>         -- Zero to max
  | '*' <min> '..' <max>   -- Min to max (inclusive)
  | '+' equivalent to '*1..'

Examples

-- Any length (0 or more hops)
MATCH (a)-[:KNOWS*]->(b)
RETURN a, b;

-- Exactly 2 hops
MATCH (a)-[:KNOWS*2]->(b)
RETURN a, b;

-- 1 to 3 hops (inclusive)
MATCH (a)-[:KNOWS*1..3]->(b)
RETURN a, b;

-- At least 2 hops
MATCH (a)-[:KNOWS*2..]->(b)
RETURN a, b;

-- Up to 5 hops (0 to 5)
MATCH (a)-[:KNOWS*..5]->(b)
RETURN a, b;

-- One or more hops (+ quantifier)
MATCH (a)-[:KNOWS+]->(b)
RETURN a, b;

Capturing Path as Variable

-- Capture entire path
MATCH path = (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b:Person)
RETURN path, length(path) AS hops;

-- Access path elements
MATCH path = (a)-[:KNOWS*]->(b)
RETURN nodes(path) AS all_nodes,
       relationships(path) AS all_rels,
       length(path) AS hop_count;

Variable-Length with Multiple Types

-- Multiple relationship types
MATCH (a)-[:KNOWS|WORKS_WITH*1..3]->(b)
RETURN a.name, b.name;

-- Undirected variable-length
MATCH (a)-[:KNOWS*1..5]-(b)
RETURN a.name, b.name;

Path Patterns

Named Paths

-- Assign path to variable
MATCH p = (a:Person)-[:KNOWS]->(b:Person)-[:WORKS_AT]->(c:Company)
RETURN p;

-- Multiple paths
MATCH
  social_path = (a)-[:KNOWS*]->(b),
  work_path = (b)-[:WORKS_AT]->(c)
RETURN social_path, work_path;

Path Functions

-- Path length (number of relationships)
MATCH p = (a)-[:KNOWS*]->(b)
RETURN length(p) AS hops;

-- All nodes in path
MATCH p = (a)-[*]->(b)
RETURN nodes(p) AS path_nodes;

-- All relationships in path
MATCH p = (a)-[*]->(b)
RETURN relationships(p) AS path_rels;

-- First and last nodes
MATCH p = (a)-[*]->(b)
RETURN head(nodes(p)) AS start_node,
       last(nodes(p)) AS end_node;

Shortest Path

shortestPath

Finds a single shortest path between two nodes.

-- Shortest path with any relationship type
MATCH p = shortestPath((a:Person {name: 'Alice'})-[*]-(b:Person {name: 'Bob'}))
RETURN p, length(p) AS distance;

-- Shortest path with specific type
MATCH p = shortestPath((a:Person {name: 'Alice'})-[:KNOWS*]-(b:Person {name: 'Bob'}))
RETURN p;

-- With maximum depth
MATCH p = shortestPath((a:Person {name: 'Alice'})-[*..10]-(b:Person {name: 'Bob'}))
RETURN p;

-- With WHERE on path
MATCH p = shortestPath((a:Person)-[:KNOWS*]-(b:Person))
WHERE a.name = 'Alice' AND b.name = 'Bob'
  AND ALL(n IN nodes(p) WHERE n.active = true)
RETURN p;

allShortestPaths

Finds all paths of the shortest length.

-- All shortest paths
MATCH p = allShortestPaths((a:Person {name: 'Alice'})-[*]-(b:Person {name: 'Bob'}))
RETURN p, length(p);

-- With constraint
MATCH p = allShortestPaths((a:City {name: 'Seattle'})-[:CONNECTED*]-(b:City {name: 'Boston'}))
WHERE ALL(r IN relationships(p) WHERE r.active = true)
RETURN p, [n IN nodes(p) | n.name] AS route;

Shortest Path Performance

ConstraintImpact
Max depth (*..N)Limits search depth
Relationship typeReduces edges to traverse
DirectionReduces edges by 50%
WHERE on pathApplied after finding paths

Best practices:

-- Always specify maximum depth for production queries
MATCH p = shortestPath((a)-[*..15]-(b))  -- Reasonable limit
RETURN p;

-- Use direction when possible
MATCH p = shortestPath((a)-[:REPORTS_TO*]->(b))  -- Only outgoing
RETURN p;

Quantified Path Patterns

Pattern Quantification

-- Repeated pattern
MATCH (a)-[:KNOWS]->{1,3}(b)  -- 1 to 3 KNOWS relationships
RETURN a, b;

-- Pattern group quantification
MATCH (a)(()-[:KNOWS]->()-[:WORKS_AT]->())+(b)
RETURN a, b;

Path Pattern Selectors

-- TRAIL: No repeated edges
MATCH TRAIL (a)-[*]-(b)
WHERE a.name = 'Alice'
RETURN b;

-- ACYCLIC: No repeated nodes
MATCH ACYCLIC (a)-[*]-(b)
WHERE a.name = 'Alice'
RETURN b;

-- SIMPLE: No repeated nodes except endpoints can match
MATCH SIMPLE (a)-[*]-(b)
RETURN a, b;

Pattern Predicates

EXISTS

Check for pattern existence without returning matches.

-- Has at least one KNOWS relationship
MATCH (p:Person)
WHERE EXISTS { (p)-[:KNOWS]->() }
RETURN p.name;

-- Complex existence check
MATCH (p:Person)
WHERE EXISTS {
  MATCH (p)-[:WORKS_AT]->(c:Company)
  WHERE c.size > 1000
}
RETURN p.name AS enterprise_employee;

COUNT in Pattern

-- Count matching patterns
MATCH (p:Person)
WHERE COUNT { (p)-[:KNOWS]->() } > 5
RETURN p.name AS popular_person;

-- Relationship count
MATCH (p:Person)
RETURN p.name, COUNT { (p)-[:KNOWS]->() } AS friend_count;

ALL, ANY, NONE, SINGLE

-- ALL nodes in path satisfy condition
MATCH p = (a)-[:KNOWS*]->(b)
WHERE ALL(n IN nodes(p) WHERE n.active = true)
RETURN p;

-- ANY node in path satisfies condition
MATCH p = (a)-[:KNOWS*]->(b)
WHERE ANY(n IN nodes(p) WHERE n.verified = true)
RETURN p;

-- NONE of the nodes satisfy condition
MATCH p = (a)-[:KNOWS*]->(b)
WHERE NONE(n IN nodes(p) WHERE n.blocked = true)
RETURN p;

-- Exactly SINGLE node satisfies condition
MATCH p = (a)-[:KNOWS*]->(b)
WHERE SINGLE(n IN nodes(p) WHERE n.admin = true)
RETURN p;

Pattern Comprehensions

List Comprehension from Pattern

-- Collect pattern results into list
MATCH (p:Person)
RETURN p.name,
       [(p)-[:KNOWS]->(friend) | friend.name] AS friend_names;

-- With filter
MATCH (p:Person)
RETURN p.name,
       [(p)-[:KNOWS]->(f:Person) WHERE f.age > 30 | f.name] AS older_friends;

-- Nested comprehension
MATCH (p:Person)
RETURN p.name,
       [(p)-[:KNOWS]->(f) | {
         name: f.name,
         mutual: [(f)-[:KNOWS]->(m) WHERE (p)-[:KNOWS]->(m) | m.name]
       }] AS friends_with_mutual;

Map Pattern Results

-- Pattern to map
MATCH (p:Person)
RETURN p.name,
       [(p)-[r:WORKED_AT]->(c:Company) | {
         company: c.name,
         from: r.start_date,
         to: r.end_date,
         duration: duration.between(r.start_date, r.end_date)
       }] AS work_history;

Complex Pattern Examples

Social Network Analysis

-- Friends of friends (not direct friends)
MATCH (me:Person {name: 'Alice'})-[:KNOWS]->(friend)-[:KNOWS]->(fof:Person)
WHERE NOT (me)-[:KNOWS]->(fof)
  AND me <> fof
RETURN DISTINCT fof.name AS recommendation;

-- Mutual friends count
MATCH (a:Person {name: 'Alice'})-[:KNOWS]->(mutual)<-[:KNOWS]-(b:Person {name: 'Bob'})
RETURN COUNT(mutual) AS mutual_friend_count;

-- Influence path
MATCH path = (influencer:Person {name: 'Alice'})-[:FOLLOWS*1..4]->(follower:Person)
WHERE follower.follower_count > 10000
RETURN path, length(path) AS degrees_of_separation;

Organizational Hierarchy

-- All reports (direct and indirect)
MATCH (ceo:Person {title: 'CEO'})<-[:REPORTS_TO*]-(employee:Person)
RETURN employee.name, length(shortestPath((employee)-[:REPORTS_TO*]->(ceo))) AS level;

-- Team hierarchy
MATCH path = (manager:Person {name: 'Alice'})<-[:REPORTS_TO*0..]-(member:Person)
RETURN [n IN nodes(path) | n.name] AS chain,
       length(path) AS depth;

-- Find common manager
MATCH (a:Person {name: 'Alice'})-[:REPORTS_TO*]->(manager:Person)<-[:REPORTS_TO*]-(b:Person {name: 'Bob'})
RETURN manager.name AS common_manager
ORDER BY length(shortestPath((a)-[:REPORTS_TO*]->(manager)))
LIMIT 1;

Graph Algorithms via Patterns

-- Triangle detection
MATCH (a)-[:KNOWS]->(b)-[:KNOWS]->(c)-[:KNOWS]->(a)
WHERE id(a) < id(b) AND id(b) < id(c)  -- Avoid duplicates
RETURN a.name, b.name, c.name;

-- Cycle detection
MATCH path = (n)-[*]->(n)
WHERE length(path) > 0
RETURN DISTINCT n.name AS node_in_cycle,
       [m IN nodes(path) | m.name] AS cycle;

-- Bridge nodes (connecting different communities)
MATCH (a:Person)-[:KNOWS]->(bridge:Person)-[:KNOWS]->(b:Person)
WHERE NOT (a)-[:KNOWS*..2]-(b)
RETURN bridge.name, COUNT(DISTINCT a) + COUNT(DISTINCT b) AS connections;

Supply Chain

-- Product dependencies
MATCH path = (product:Product {name: 'Widget'})-[:REQUIRES*]->(component:Part)
RETURN [n IN nodes(path) | n.name] AS dependency_chain;

-- Critical path
MATCH path = (start:Task {name: 'Start'})-[:DEPENDS_ON*]->(end:Task {name: 'End'})
WITH path, REDUCE(total = 0, t IN nodes(path) | total + t.duration) AS path_duration
RETURN path, path_duration
ORDER BY path_duration DESC
LIMIT 1;

-- Single point of failure
MATCH (p:Part)<-[:REQUIRES*]-(products:Product)
WITH p, COUNT(DISTINCT products) AS dependent_products
WHERE dependent_products > 10
RETURN p.name AS critical_component, dependent_products;

Performance Optimization

Pattern Selectivity

Order patterns from most selective to least selective:

-- Good: Start with selective pattern
MATCH (p:Person {email: 'alice@example.com'})  -- Unique, indexed
MATCH (p)-[:KNOWS*1..3]->(friend)             -- Expansion after
RETURN friend;

-- Bad: Start with broad pattern
MATCH (friend:Person)                          -- All persons
MATCH (p:Person {email: 'alice@example.com'})-[:KNOWS*1..3]->(friend)
RETURN friend;

Limiting Variable-Length Paths

-- Always set reasonable bounds
MATCH (a)-[:KNOWS*1..5]->(b)  -- Max 5 hops

-- Use LIMIT early
MATCH (a:Person {name: 'Alice'})-[:KNOWS*]->(b)
RETURN b
LIMIT 100;

Using EXPLAIN

EXPLAIN
MATCH (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b)
RETURN b.name;

-- Check for:
-- 1. Index usage on starting node
-- 2. Estimated rows at each step
-- 3. Expansion operator type

Pattern Matching Best Practices

1. Anchor Your Patterns

-- Good: Start from a specific node
MATCH (start:Person {id: 123})-[:KNOWS*]->(end)
RETURN end;

-- Avoid: Unanchored patterns
MATCH ()-[:KNOWS*]->()  -- Scans everything

2. Use Direction When Possible

-- Better: Directed
MATCH (a)-[:FOLLOWS]->(b)

-- Slower: Undirected (doubles the search space)
MATCH (a)-[:FOLLOWS]-(b)

3. Filter Early

-- Good: Filter in pattern
MATCH (p:Person {active: true})-[:KNOWS]->(f)
RETURN f;

-- Less efficient: Filter after
MATCH (p:Person)-[:KNOWS]->(f)
WHERE p.active = true
RETURN f;

4. Avoid Unbounded Paths in Production

-- Dangerous: Can traverse entire graph
MATCH (a)-[*]->(b)

-- Safe: Bounded depth
MATCH (a)-[*..10]->(b)


Last Updated: January 28, 2026 Geode Version: v0.1.3+ ISO GQL Conformance Profile: ISO/IEC 39075:2024 compliance (see conformance profile)