The dashes and arrowheads around it give the direction. There are three:
-[edge_body]-> left to right <-[edge_body]- right to left -[edge_body]- either direction
bold keywords · italic placeholders you supply · [ ] optional · ... repeatable. See Syntax conventions.
## Description
`MATCH` reads the graph. It emits one row for every way its pattern can be laid
over the stored vertices and edges. Each named element of the pattern is bound
to the vertex, edge or path it matched. Later clauses in the pipeline see those
bindings. A `MATCH` in the middle of a pipeline runs once per incoming row and
joins its matches to that row, the way a join adds rows to a relational query.
- A pattern element with **no variable** is still matched. It just cannot be
referred to later. Leave the variable out when you only need the element to
constrain the shape.
- A **label** restricts an element to that label and its child labels; `ONLY`
restricts it to the label itself. Without a label, an element matches every
vertex or every edge in the graph.
- A **property map** in the pattern is an equality test on each key it lists. It
is shorthand: `(m:movie {year: 1999})` and
`(m:movie) WHERE m.year = 1999` match the same vertices.
- **Commas** separate independent patterns inside one `MATCH`. Patterns that
share a variable are joined on it; patterns that share nothing produce their
Cartesian product.
- Within one pattern match **no edge is used twice**. Node bindings may repeat,
so a pattern can close a triangle, but a two-hop pattern never doubles back
along the edge it arrived on.
- An **edge variable** may be used once per pattern; reusing it raises
`duplicate variable`. A **node variable** may be repeated, which constrains
both positions to the same vertex.
- `MATCH` may lead a query or sit anywhere after the first clause. It never ends
one: a query has to finish with `RETURN`, `FINISH` or a write clause.
`OPTIONAL MATCH` keeps input rows for which the pattern finds nothing, binding
the pattern's variables to null instead of dropping the row. It has its own page,
[`OPTIONAL MATCH`](optional-match.md).
## Parameters
pattern
: One path pattern, optionally preceded by `path_variable =`. Several patterns
may be listed, separated by commas.
path_variable
: Name bound to the whole matched path, as a value carrying its vertices and
edges in order. Read it with the path functions `length()`, `nodes()` and
`edges()`. The name must be an identifier: an unreserved keyword such as `path`
is rejected here unless you quote it (`` "path" ``).
variable
: Name bound to the matched vertex or edge. Optional. Visible to every clause
after this one.
label
: A vertex label on a node pattern, an edge label on an edge pattern. One label
per element.
ONLY
: Excludes child labels, so the element matches rows of that label's own table
and nothing that inherits from it. Requires a label.
property_map
: A map literal, or a query parameter holding one. Each key is compared for
equality against the element's property of that name.
min, max
: Bounds on the number of edges a variable-length edge pattern traverses. `*n`
is exactly *n* hops; `*m..n` is between *m* and *n*; `*m..` is *m* or more;
`*..n` is up to *n*; a bare `*` is one or more.
condition
: A boolean expression filtering the matches, evaluated with every variable in
the pattern bound. See [`WHERE`](where.md).
## Examples
Every example runs against [the example dataset](../../get_started/example-dataset.md).
### Find one vertex by a property
```cypher
MATCH (p:person {name: 'Keanu Reeves'})
RETURN p;
```
```
p
---------------------------------------------------
person[4.1]{"born": 1964, "name": "Keanu Reeves"}
(1 row)
```
A vertex prints as `label[id]{properties}`. `4.1` is its graph id, which you read
with `id(p)`. `p.id` would read a property named `id`, and there is none.
### Follow an edge in one direction
```cypher
MATCH (p:person)-[r:acted_in]->(m:movie {title: 'The Matrix'})
RETURN p.name AS actor, r.role AS role
ORDER BY actor;
```
```
actor | role
----------------------+------------
"Carrie-Anne Moss" | "Trinity"
"Keanu Reeves" | "Neo"
"Laurence Fishburne" | "Morpheus"
(3 rows)
```
Both endpoints and the edge carry a label, and the edge is bound to `r` so its
`role` property can be projected. String values come back quoted because a Cypher
`RETURN` produces jsonb.
### Leave out the elements you do not need
```cypher
MATCH (p:person {name: 'Christopher Nolan'})-[:directed]->(m)
RETURN m.title AS film
ORDER BY film;
```
```
film
----------------
"Inception"
"Interstellar"
(2 rows)
```
The edge has no variable and the far endpoint has no label. Both still constrain
the match; neither costs a binding.
### Match an edge in either direction
An edge pattern with no arrowhead matches the edge whichever way it points:
```cypher
MATCH (m:movie {title: 'Total Recall'})-[:related_to]-(n:movie)
RETURN n.title AS neighbour
ORDER BY neighbour;
```
```
neighbour
--------------
"Inception"
"The Matrix"
(2 rows)
```
`Total Recall` has one outgoing `related_to` edge and one incoming one. Writing
arrowheads at both ends, `<-[:related_to]->`, means the same thing.
### Join two patterns on a shared variable
```cypher
MATCH (a:person)-[:acted_in]->(m:movie), (d:person)-[:directed]->(m)
WHERE m.title = 'The Matrix'
RETURN a.name AS actor, d.name AS director
ORDER BY actor, director;
```
```
actor | director
----------------------+-------------------
"Carrie-Anne Moss" | "Lana Wachowski"
"Carrie-Anne Moss" | "Lilly Wachowski"
"Keanu Reeves" | "Lana Wachowski"
"Keanu Reeves" | "Lilly Wachowski"
"Laurence Fishburne" | "Lana Wachowski"
"Laurence Fishburne" | "Lilly Wachowski"
(6 rows)
```
Both patterns mention `m`, so they are joined on it. Two consecutive `MATCH`
clauses would give the same result; so would one pattern that meets in the
middle, `(a:person)-[:acted_in]->(m:movie)<-[:directed]-(d:person)`.
### Name the whole path
Put `variable =` in front of a pattern to bind the path itself:
```cypher
MATCH p = (a:movie {title: 'The Matrix'})-[:related_to*1..3]->(b:movie)
RETURN length(p) AS hops, [x IN nodes(p) | x.title] AS route
ORDER BY hops, route;
```
```
hops | route
------+---------------------------------------------------------------------------------------------
1 | ["The Matrix", "The Matrix Reloaded"]
1 | ["The Matrix", "The Terminator"]
1 | ["The Matrix", "Total Recall"]
2 | ["The Matrix", "The Matrix Reloaded", "The Matrix Revolutions"]
2 | ["The Matrix", "The Terminator", "Terminator 2: Judgment Day"]
2 | ["The Matrix", "Total Recall", "Inception"]
3 | ["The Matrix", "The Matrix Reloaded", "The Matrix Revolutions", "The Matrix Resurrections"]
(7 rows)
```
### Walk a variable number of edges
`*` on an edge pattern repeats it. Each bound is optional:
```cypher
MATCH (m:movie {title: 'The Matrix'})-[:related_to*2]->(n:movie)
RETURN n.title AS reached
ORDER BY reached;
```
```
reached
------------------------------
"Inception"
"Terminator 2: Judgment Day"
"The Matrix Revolutions"
(3 rows)
```
A lower bound of `0` includes the zero-length path, so the starting vertex is one
of the answers:
```cypher
MATCH (m:movie {title: 'The Matrix'})-[:related_to*0..1]->(n:movie)
RETURN n.title AS reached
ORDER BY reached;
```
```
reached
-----------------------
"The Matrix"
"The Matrix Reloaded"
"The Terminator"
"Total Recall"
(4 rows)
```
### Exclude child labels with ONLY
A label matches its child labels too, because a label is a table and a child
label inherits from it. `ONLY` narrows the match to the one label. Give `person` a
child label to see the difference:
```cypher
CREATE VLABEL crew INHERITS (person);
CREATE (:crew {name: 'Bill Pope', born: 1952});
```
```
CREATE VLABEL
UPDATE 1
```
```cypher
MATCH (p:person) RETURN count(*) AS people;
```
```
people
--------
29
(1 row)
```
```cypher
MATCH (p:person ONLY) RETURN count(*) AS people;
```
```
people
--------
28
(1 row)
```
`ONLY` goes after the label and before the property map, as in
`(p:person ONLY {name: 'Keanu Reeves'})`. It works the same on an edge pattern,
`-[r:acted_in ONLY]->`. Undo the setup with:
```cypher
MATCH (p:crew) DETACH DELETE p;
DROP VLABEL crew;
```
```
UPDATE 1
DROP VLABEL
```
### Filter the matches with WHERE
`WHERE` attached to a `MATCH` is part of the match, not a later step:
```cypher
MATCH (m:movie) WHERE m.year > 2015
RETURN m.title AS film, m.year AS year
ORDER BY year, film;
```
```
film | year
----------------------------+------
"Blade Runner 2049" | 2017
"The Matrix Resurrections" | 2021
(2 rows)
```
### Match edges of several labels
An edge pattern carries at most one label. Listing two raises an error:
```cypher
MATCH (p:person)-[r:acted_in|directed]->(m:movie) RETURN count(r);
```
```
ERROR: multiple types for relationship not supported
```
Match an unlabelled edge instead and test its label in `WHERE`:
```cypher
MATCH (p:person)-[r]->(m:movie)
WHERE label(r) IN ['acted_in', 'directed']
RETURN count(r) AS edges;
```
```
edges
-------
46
(1 row)
```
`label()` returns the element's label as a string. `IN` over a list of names is
therefore the general form, and the only way to match a set of edge labels.
## Notes
A label test belongs in `label(n) = '...'`, not in a colon in `WHERE`. Writing
`WHERE n:movie` is a syntax error, and so is a bare pattern used as a predicate:
`WHERE (m)-[:related_to]->(:movie)` does not parse. Use
`WHERE EXISTS { MATCH (m)-[:related_to]->(:movie) }` to test for a pattern's
existence.
A property map compares jsonb values, and so does a comparison against a
property. That matters when the literal's type does not match the stored one.
See [`WHERE`](where.md).
```{note}
The named-path variable is parsed more narrowly than an element variable. `(path)`
is an acceptable node variable, but `MATCH path = ...` is a syntax error because
`path` is a keyword; write `MATCH p = ...` or quote the name.
```
## Compatibility
### Label expressions (ISO/IEC 39075 §16.8)
The standard's `