Skip to content

Commit 8b1d534

Browse files
committed
Unify Path Pattern syntax with normal Pattern syntax
1 parent 9d3caa9 commit 8b1d534

File tree

1 file changed

+79
-77
lines changed

1 file changed

+79
-77
lines changed

cip/1.accepted/CIP2017-02-06-Regular-Path-Patterns.adoc

+79-77
Original file line numberDiff line numberDiff line change
@@ -17,83 +17,88 @@ In Cypher Regular Path Queries are expressed through the use of _Regular Path Pa
1717
A Regular Path Pattern is defined as:
1818

1919
• A simple relationship type +
20-
`()-/:X/-()` denotes a Regular Path Pattern matching relationships of type `X`.
20+
`()-[:X]-()` denotes a Regular Path Pattern matching relationships of type `X`.
2121
• A predicate on the labels of a node +
22-
`()-/(:Z)/-()` denotes a Regular Path Pattern matching nodes with label `Z`.
22+
`()-[(:Z)]-()` denotes a Regular Path Pattern matching nodes with label `Z`.
2323
• A sequence of Regular Path Patterns +
24-
`()-/_a_ _b_/-()` denotes a Regular Path Pattern matching first the pattern defined by `_a_`, then the pattern defined by `_b_` (in order left to right).
24+
`()-[_a_ _b_]-()` denotes a Regular Path Pattern matching first the pattern defined by `_a_`, then the pattern defined by `_b_` (in order left to right).
2525
• An alternative between Regular Path Patterns +
26-
`()-/_a_ | _b_/-()` denotes a Regular Path Pattern matching either the pattern defined by `_a_` or the pattern defined by `_b_`.
26+
`()-[_a_ | _b_]-()` denotes a Regular Path Pattern matching either the pattern defined by `_a_` or the pattern defined by `_b_`.
2727
• A repetition of a Regular Path Pattern +
28-
`()-/_a_*/-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` zero or more times. +
29-
`()-/_a_+/-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` one or more times. +
30-
`()-/_a_*_x_../-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` `_x_` or more times. +
31-
`()-/_a_*_x_.._y_/-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` at least `_x_` times and at most `_y_` times.
28+
`()-[_a_*]-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` zero or more times. +
29+
`()-[_a_+]-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` one or more times. +
30+
`()-[_a_*_x_..]-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` `_x_` or more times. +
31+
`()-[_a_*_x_.._y_]-()` denotes a Regular Path Pattern matching the pattern defined by `_a_` at least `_x_` times and at most `_y_` times.
3232
• A grouping of a Regular Path Pattern +
33-
`()-/[_a_]/-()` denotes a grouping of the pattern `_a_`.
33+
`()-[[_a_]]-()` denotes a grouping of the pattern `_a_`.
3434
• A specification of direction for a Regular Path Pattern +
35-
`()-/ _a_ >/-()` denotes that the Regular Path Pattern `_a_` should be interpreted in a left-to-right direction. +
36-
`()-/< _a_ /-()` denotes that the Regular Path Pattern `_a_` should be interpreted in a right-to-left direction. +
37-
`()-/< _a_ >/-()` denotes that the Regular Path Pattern `_a_` should be interpreted in any direction.
35+
`()-[ _a_ >]-()` denotes that the Regular Path Pattern `_a_` should be interpreted in a left-to-right direction. +
36+
`()-[< _a_ ]-()` denotes that the Regular Path Pattern `_a_` should be interpreted in a right-to-left direction. +
37+
`()-[< _a_ >]-()` denotes that the Regular Path Pattern `_a_` should be interpreted in any direction.
3838
• A reference to a Defined Path Predicate +
39-
`()-/alpha/-()` denotes a reference to a Defined Path Predicate named `alpha`.
39+
`()-[alpha]-()` denotes a reference to a Defined Path Predicate named `alpha`.
4040

41-
Regular Path Patterns are written similarly to how relationship patterns are written, but enclosed within two slash (`/`) characters instead of brackets (`[]`).
42-
43-
Contrary to Relationship Patterns, Regular Path Patterns do _not_ allow binding a relationship to a variable.
44-
In order to bind the matching path to a variable, a Path Assignment should be used, by preceding the path with an identifier and an equals sign (`=`).
45-
This avoids a problem that existed in the past with repetition of relationships (a syntax that is deprecated with the introduction of Regular Path Patterns), where a relationship variable would bind to a list, making it hard to express predicates over the actual relationships.
41+
Binding of a relationship to a variable is only allowed in the most simple case of a Path Pattern, where only a single relationship is matched by the pattern.
42+
For binding a whole path to a variable, Path Assignment should be used, by preceding the path with an identifier and an equals sign (`=`).
43+
This avoids a problem that existed in the past with repetition of relationships (a syntax that is unsupported as of the introduction of Regular Path Patterns), where a relationship variable would bind to a list, making it hard to express predicates over the actual relationships.
4644
Predicates on parts of a Regular Path Pattern are instead expressed through the use of explicitly defined path predicates.
4745

4846
=== Syntax
4947

50-
The syntax of Regular Path Patterns fit into the greater Cypher syntax through `PatternElementChain`.
48+
Regular Path Patterns are part of the Pattern syntax of Cypher.
5149

50+
[source, ebnf]
5251
----
53-
PatternElementChain = (RelationshipPattern | RegularPathPattern), NodePattern ;
52+
Pattern = PathPattern, {',', PathPattern} ;
53+
PathPattern = [Variable, '='], NodePattern, {RegularPathPattern, NodePattern} ;
54+
55+
NodePattern = '(', [Variable], [NodeLabels], [Properties], ')' ;
5456
55-
RegularPathPattern = (LeftArrowHead, Dash, '/', [RegularPathExpression], '/', Dash, RightArrowHead)
56-
| (LeftArrowHead, Dash, '/', [RegularPathExpression], '/', Dash)
57-
| (Dash, '/', [RegularPathExpression], '/', Dash, RightArrowHead)
58-
| (Dash, '/', [RegularPathExpression], '/', Dash)
57+
RegularPathPattern = (LeftArrowHead, Dash, '[', [RegularPathExpression], ']', Dash, RightArrowHead)
58+
| (LeftArrowHead, Dash, '[', [RegularPathExpression], ']', Dash)
59+
| (Dash, '[', [RegularPathExpression], ']', Dash, RightArrowHead)
60+
| (Dash, '[', [RegularPathExpression], ']', Dash)
5961
;
60-
RegularPathExpression = {RegularPathAlternative}- ;
61-
RegularPathAlternative = RegularPathSequence, {'|', RegularPathSequence} ;
62-
RegularPathSequence = {RegularPathStar}- ;
63-
RegularPathStar = RegularPathDirected [('*', [RangeLiteral]) | '+'] ;
62+
63+
RegularPathExpression = {RegularPathAlternative} | BoundEdge ;
64+
RegularPathAlternative = RegularPathRepetition, {'|', RegularPathRepetition} ;
65+
RegularPathRepetition = RegularPathDirected, [('*', [RangeDetail]) | '+' | '?'] ;
6466
RegularPathDirected = ['<'], RegularPathBase, ['>'] ;
65-
RegularPathBase = RegularPathRelationship
66-
| RegularPathAnyRelationship
67+
RegularPathBase = RegularPathEdge
68+
| RegularPathAny
6769
| RegularPathNode
6870
| RegularPathReference
69-
| '[' RegularPathExpression ']'
71+
| ('[', RegularPathExpression, ']')
7072
;
71-
RegularPathRelationship = RelType ;
72-
RegularPathAnyRelationship = '-' ;
73-
RegularPathNode = '(' NodeLabels ')' ;
74-
RegularPathReference = SymbolicName ;
73+
RegularPathEdge = (EdgeLabels, [Properties]) | Properties ;
74+
RegularPathAny = '-' ;
75+
RegularPathNode = '(', [NodeLabels], [Properties], ')' ;
76+
RegularPathReference = '~', SymbolicName ;
77+
78+
BoundEdge = Variable, [EdgeLabels], [Properties] ;
79+
EdgeLabels = ':', LabelName, {'|', LabelName} ;
80+
NodeLabels = ':', LabelName, {':', LabelName} ;
81+
LabelName = SymbolicName ;
82+
83+
RangeDetail = [IntegerLiteral | Parameter], '..', [IntegerLiteral | Parameter] ;
7584
----
7685

7786
The `RegularPathReference` is a reference to a Defined Path Predicate.
7887
These are defined using the following syntax:
7988

89+
[source, ebnf]
8090
----
81-
DefinedPathPredicate = 'PATH' PathPredicatePrototype, 'IS', Pattern, [Where] ;
82-
PathPredicatePrototype = '(', Variable, ')', RegularPathPrototype, '(', Variable, ')' ;
83-
RegularPathPrototype = (LeftArrowHead, Dash, '/', DefinedPathName, '/', Dash)
84-
| (Dash, '/', DefinedPathName, '/', Dash, RightArrowHead)
85-
| (Dash, '/', DefinedPathName, '/', Dash)
86-
;
91+
DefinedPathPredicate = 'PATH', 'PATTERN', DefinedPathName, '=', PathPattern, [Where] ;
8792
DefinedPathName = SymbolicName ;
8893
----
8994

9095

9196
=== Directions
9297

9398
The direction of relationships matched by a Regular Path Pattern is primarily decided by the directional arrow surrounding the pattern.
94-
If the arrow points from left to right (i.e. `(left)-/pattern/\->(right)`), the paths described by the pattern are paths in the left-to-right direction, i.e. paths that are _outgoing_ from the node to the left of the pattern, and _incoming_ to the node to the right of the pattern.
95-
If the arrow points from right to left (i.e. `(left)\<-/pattern/-(right)`), the paths described by the pattern are paths in the right-to-left paths direction, i.e. paths that are _incoming_ to the node to the left of the pattern, and _outgoing_ from the node to the right of the pattern.
96-
If there are no arrowheads (i.e. `(left)-/pattern/-(right)`), or if both arrowheads are present (i.e. `(left)\<-/pattern/\->(right)`), the paths described by the pattern are paths in either the left-to-right or the right-to-left direction.
99+
If the arrow points from left to right (i.e. `(left)-[pattern]\->(right)`), the paths described by the pattern are paths in the left-to-right direction, i.e. paths that are _outgoing_ from the node to the left of the pattern, and _incoming_ to the node to the right of the pattern.
100+
If the arrow points from right to left (i.e. `(left)\<-[pattern]-(right)`), the paths described by the pattern are paths in the right-to-left paths direction, i.e. paths that are _incoming_ to the node to the left of the pattern, and _outgoing_ from the node to the right of the pattern.
101+
If there are no arrowheads (i.e. `(left)-[pattern]-(right)`), or if both arrowheads are present (i.e. `(left)\<-[pattern]\->(right)`), the paths described by the pattern are paths in either the left-to-right or the right-to-left direction.
97102

98103
All parts of a Regular Path Pattern will assume the direction of the surrounding arrow, unless the direction is explicitly overridden for that particular part of the pattern.
99104
A prefix of `<` to part of a pattern overrides the direction of that part to be right-to-left.
@@ -103,7 +108,7 @@ Direction overrides only apply to a single pattern part.
103108
In order to apply the direction override to multiple parts of the pattern, those parts should be grouped.
104109

105110
Using both a `<` prefix and a `>` suffix on the same pattern is always the same thing as a disjunction between that pattern with a `<` prefix and that pattern with a `>` suffix.
106-
This means that `()-/< _a_ >/-()` is the same as `()-/[< _a_] | [_a_ >]/-()`.
111+
This means that `()-[< _a_ >]-()` is the same as `()-[[< _a_] | [_a_ >]]-()`.
107112

108113
==== Directions and Defined Path Predicates
109114

@@ -116,63 +121,60 @@ A Defined Path Predicate declared without a direction must have a definition tha
116121

117122
==== Direction examples
118123

119-
• `()-/a <[b c] d/\->()` is the same as `()-/a/\->()\<-/b c/-()-/d/\->(d)`, i.e. the direction of the group `b c` has been overridden to be right-to-left in a pattern where the overall direction is left-to-right.
120-
• `()-/a <b> c/\->()` is the same as `()-/a/\->()-/b/-()-/c/\->()`, i.e. the direction of `b` has been overridden to be _either direction_.
121-
• `()-/a/-()`, `()-/<a>/-()`, `()-/<a>/\->()`, `()\<-/<a>/-()`, `()\<-/<a>/\->()`, and `()\<-/a/\->()` all mean the same thing: matching `a` in _either direction_.
124+
• `()-[a <[b c] d]\->()` is the same as `()-[a]\->()\<-[b c]-()-[d]\->(d)`, i.e. the direction of the group `b c` has been overridden to be right-to-left in a pattern where the overall direction is left-to-right.
125+
• `()-[a <b> c]\->()` is the same as `()-[a]\->()-[b]-()-[c]\->()`, i.e. the direction of `b` has been overridden to be _either direction_.
126+
• `()-[a]-()`, `()-[<a>]-()`, `()-[<a>]\->()`, `()\<-[<a>]-()`, `()\<-[<a>]\->()`, and `()\<-[a]\->()` all mean the same thing: matching `a` in _either direction_.
122127

123128
Given these Defined Path Predicates:
124129

125130
[source, cypher]
126131
----
127-
PATH (l)-/alpha/->(r) IS (l)-[:X]->()-[:Y]->(r)
128-
PATH (l)-/beta/->(r) IS (l)<-[:Y]-()<-[:X]-(r)
129-
PATH (l)-/gamma/-(r) IS (l)-/[:X :Y]> | <[:Y :X]/-(r)
132+
PATH (l)-[alpha]->(r) IS (l)-[:X]->()-[:Y]->(r)
133+
PATH (l)-[beta]->(r) IS (l)<-[:Y]-()<-[:X]-(r)
134+
PATH (l)-[gamma]-(r) IS (l)-[[:X :Y]> | <[:Y :X]]-(r)
130135
----
131136

132-
• `()-/alpha/\->()` is equivalent to `()\<-/beta/-()`
133-
• `()\<-/alpha/-()` is equivalent to `()-/beta/\->()`
134-
• `()-/gamma/\->()` is equivalent to `()\<-/gamma/-()`, since both are equivalent to `()-/gamma/-()`
135-
• `()-/gamma/-()` is equivalent to `()-/alpha/-()`, since `()-/alpha/-()` is the same as `()-/alpha> | <alpha/-()`, which is equivalent to the declaration of `gamma`. +
136-
It is also equivalent to `()-/<beta | beta>/-()` which is the same as `()-/beta/-()`.
137+
• `()-[alpha]\->()` is equivalent to `()\<-[beta]-()`
138+
• `()\<-[alpha]-()` is equivalent to `()-[beta]\->()`
139+
• `()-[gamma]\->()` is equivalent to `()\<-[gamma]-()`, since both are equivalent to `()-[gamma]-()`
140+
• `()-[gamma]-()` is equivalent to `()-[alpha]-()`, since `()-[alpha]-()` is the same as `()-[alpha> | <alpha]-()`, which is equivalent to the declaration of `gamma`. +
141+
It is also equivalent to `()-[<beta | beta>]-()` which is the same as `()-[beta]-()`.
137142

138143
=== Regular Path Pattern Examples
139144

140145
The astute reader of the syntax will have noticed that it is possible to express a Regular Path Pattern with an empty path expression:
141146

142147
[source, cypher]
143148
----
144-
MATCH (a)-//-(b)
149+
MATCH (a)-[]-(b)
145150
----
146151

147-
This pattern simply states that `a` and `b` must be the same node, and is thus the same as:
148-
149-
[source, cypher]
150-
----
151-
MATCH (a), (b) WHERE a = b
152-
----
152+
The semantics of this query is to match any single relationship between `a` and `b`.
153+
It is thus equivalent to `(a)-[-]-(b)` or `(a)--(b)`.
153154

154-
The same reader will also have noticed that it is possible to define a pattern containing just a relationship type:
155+
It is possible to express a completely empty pattern, a pattern that matches `a` and `b` to the same node.
156+
This is done by using only a single node predicate in the path pattern:
155157

156158
[source, cypher]
159+
.A pattern matching a path of length 0
157160
----
158-
MATCH (a)-/:KNOWS/->(b)
161+
MATCH (a)-[()]-(b)
159162
----
160163

161-
That pattern is indeed equivalent to the very similar relationship pattern:
164+
This pattern states that `a` and `b` must be the same node, by virtue of stating a pattern that matches any node.
165+
It is thus the same as:
162166

163167
[source, cypher]
164168
----
165-
MATCH (a)-[:KNOWS]->(b)
169+
MATCH (a), (b) WHERE a = b
166170
----
167171

168-
The main difference being that the variant with a relationship pattern is able to bind that relationship and express further predicates over it.
169-
170172
The Regular Path Patterns start becoming interesting when larger expressions are put together:
171173

172174
[source, cypher]
173175
.Finding someone loved by someone hated by someone you know, transitively
174176
----
175-
MATCH (you)-/[:KNOWS :HATES]+ :LOVES/->(someone)
177+
MATCH (you)-[[:KNOWS :HATES]+ :LOVES]->(someone)
176178
----
177179

178180
Note the `+` expressing one or more occurrences of the sequence `KNOWS` followed by `HATES`.
@@ -185,7 +187,7 @@ It is possible to both prefix the part with `<` and suffix it with `>`, indicati
185187
[source, cypher]
186188
.Specifying the direction for different parts of the pattern
187189
----
188-
MATCH (you)-/[:KNOWS <:HATES]+ :LOVES/->(someone)
190+
MATCH (you)-[[:KNOWS <:HATES]+ :LOVES]->(someone)
189191
----
190192

191193
In the example above we say that the `HATES` relationships should have the opposite direction to the other relationships in the path.
@@ -195,8 +197,8 @@ Through the use of Defined Path Predicates we can express even more predicates o
195197
[source, cypher]
196198
.Find a chain of unreciprocated lovers
197199
----
198-
MATCH (you)-/unreciprocated_love*/->(someone)
199-
PATH (a)-/unreciprocated_love/->(b) IS
200+
MATCH (you)-[unreciprocated_love*]->(someone)
201+
PATH (a)-[unreciprocated_love]->(b) IS
200202
(a)-[:LOVES]->(b)
201203
WHERE NOT EXISTS { (b)-[:LOVES]->(a) }
202204
----
@@ -209,8 +211,8 @@ This can be achieved by using a Defined Path Predicate where the nodes on both e
209211
[source, cypher]
210212
.Find friends of friends that are not haters
211213
----
212-
MATCH (you)-/:KNOWS not_a_hater :KNOWS/-(friendly_friend_of_friend)
213-
PATH (x)-/not_a_hater/-(x) IS (x)
214+
MATCH (you)-[:KNOWS not_a_hater :KNOWS]-(friendly_friend_of_friend)
215+
PATH (x)-[not_a_hater]-(x) IS (x)
214216
WHERE NOT EXISTS { (x)-[:HATES]->() }
215217
----
216218

@@ -222,8 +224,8 @@ This is obviously the case when both nodes are the same, but it would also be th
222224
[source, cypher]
223225
.Find chains of co-authorship
224226
----
225-
MATCH (you)-/co_author*/-(someone)
226-
PATH (a)-/co_author/-(b) IS
227+
MATCH (you)-[co_author*]-(someone)
228+
PATH (a)-[co_author]-(b) IS
227229
(a)-[:AUTHORED]->(:Book)<-[:AUTHORED]-(b)
228230
WHERE a <> b
229231
----

0 commit comments

Comments
 (0)