Skip to content

Commit 8b4efc1

Browse files
committed
not a path pattern expression
1 parent d0507dd commit 8b4efc1

File tree

6 files changed

+588
-0
lines changed

6 files changed

+588
-0
lines changed

modules/ROOT/content-nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
*** xref:expressions/predicates/comparison-operators.adoc[]
7777
*** xref:expressions/predicates/list-operators.adoc[]
7878
*** xref:expressions/predicates/string-operators.adoc[]
79+
*** xref:expressions/predicates/label-expression-predicates.adoc[]
7980
*** xref:expressions/predicates/path-pattern-expressions.adoc[]
8081
*** xref:expressions/predicates/type-predicate-expressions.adoc[]
8182
** xref:expressions/node-relationship-operators.adoc[]

modules/ROOT/images/label_expression_predicates_graph.svg

Lines changed: 280 additions & 0 deletions
Loading

modules/ROOT/pages/expressions/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ For details and examples of specific expressions, see the following sections:
1010
** xref:expressions/predicates/comparison-operators.adoc[]: `=`, `<>`, `<`, `>`, `\<=`, `>=`, `IS NULL`, `IS NOT NULL`
1111
** xref:expressions/predicates/list-operators.adoc[]: `IN`
1212
** xref:expressions/predicates/string-operators.adoc[]: `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `IS NORMALIZED`, `IS NOT NORMALIZED`, `=~`
13+
** xref:expressions/predicates/label-expression-predicates.adoc[]: information about how to test whether a node or relationship matches a label expression or not.
1314
** xref:expressions/predicates/path-pattern-expressions.adoc[]: information about filtering queries with path pattern expressions.
1415
** xref:expressions/predicates/type-predicate-expressions.adoc[]: information about how to verify the value type of a Cypher expression.
1516
* xref:expressions/node-relationship-operators.adoc[]: information about how to access `NODE` and `RELATIONSHIP` property values with `.` and `[]`.

modules/ROOT/pages/expressions/predicates/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This chapter is divided into the following sections:
99
* xref:expressions/predicates/comparison-operators.adoc[]: `=`, `<>`, `<`, `>`, `\<=`, `>=`, `IS NULL`, `IS NOT NULL`
1010
* xref:expressions/predicates/list-operators.adoc[]: `IN`
1111
* xref:expressions/predicates/string-operators.adoc[]: `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `IS NORMALIZED`, `IS NOT NORMALIZED`, `=~`
12+
* xref:expressions/predicates/label-expression-predicates.adoc[]: information about how to test whether a node or relationship matches a label expression or not.
1213
* xref:expressions/predicates/path-pattern-expressions.adoc[]: information about filtering queries with path pattern expressions.
1314
* xref:expressions/predicates/type-predicate-expressions.adoc[]: information about how to verify the value type of a Cypher expression.
1415
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
= Label expression predicates
2+
:description: This page describes how to use label expression predicates with Cypher.
3+
4+
A label expression predicate can be used to verify that the labels of a node or the relationship type of a relationship matches a given label expression.
5+
6+
[[label-expression-predicat-syntax]]
7+
== Syntax
8+
9+
[source, syntax]
10+
----
11+
<expr>:<label-expression>
12+
----
13+
14+
Where `<expr>` is any Cypher expression and `<label-expression>` is any Cypher xref::patterns/reference.adoc#label-expressions[label expression].
15+
16+
[[example-graph]]
17+
== Example graph
18+
19+
The following graph is used for the examples below:
20+
21+
image::label_expression_predicates_graph.svg[width="700",role="middle"]
22+
23+
To recreate the graph, run the following query against an empty Neo4j database:
24+
25+
[source, cypher]
26+
----
27+
CREATE (alice:Person&Manager {name:'Alice', age: 65, skills: ['Java', 'Python']}),
28+
(cecil:Person&Developer {name: 'Cecil', age: 25, skills: ['Java', 'Python']}),
29+
(cecilia:Person&Developer {name: 'Cecilia', age: 31, skills: ['JavaScript', 'TypeScript']}),
30+
(charlie:Person&Engineer {name: 'Charlie', age: 61, skills: ['C++', 'Python']}),
31+
(daniel:Person&Director {name: 'Daniel', age: 39, skills: ['JavaScript', 'Slides']}),
32+
(eskil:Person&CEO {name: 'Eskil', age: 39, skills: ['Slides', 'ChatGPT']}),
33+
34+
(cecil)-[:WORKS_FOR]->(alice),
35+
(cecilia)-[:WORKS_FOR]->(alice),
36+
(charlie)-[:WORKS_FOR]->(daniel),
37+
(alice)-[:REPORTS_TO]->(daniel),
38+
(daniel)-[:REPORTS_TO]->(eskil)
39+
----
40+
41+
[node-label-expression-predicate]
42+
== Test that node has a certain labels
43+
44+
Given `p` is a node, `p:Manager` tests whether node `p` has the label `Manager` or not and results in `true` or `false`, respectively.
45+
46+
// tag::node-label-expression-predicate-1[]
47+
[source, cypher]
48+
----
49+
MATCH (p:Person)
50+
RETURN p.name AS name, p:Manager AS isManager
51+
----
52+
// end::node-label-expression-predicate-1[]
53+
54+
[role="queryresult",options="header,footer",cols="2*<m"]
55+
|===
56+
| name | isManager
57+
58+
| "Alice" | true
59+
| "Cecil" | false
60+
| "Cecilia" | false
61+
| "Charlie" | false
62+
| "Daniel" | false
63+
| "Eskil" | false
64+
65+
2+d|Rows: 6
66+
|===
67+
68+
Given `p` is a node and a more complex xref::patterns/reference.adoc#label-expressions[label expression], e.g, `Manager|Director|CEO`, `p:Manager|Director|CEO` tests whether node `p` matches the label expression `Manager|Director|CEO` or not and results in `true` or `false`, respectively.
69+
More specifically, `p:Manager|Director|CEO` tests whether `p` has at least one of the three labels `Manager`, `Director`, and `CEO`.
70+
71+
// tag::node-label-expression-predicate-2[]
72+
[source, cypher]
73+
----
74+
MATCH (p:Person)
75+
RETURN p.name AS name,
76+
p:Manager|Director|CEO AS isManager
77+
----
78+
// end::node-label-expression-predicate-2[]
79+
80+
[role="queryresult",options="header,footer",cols="2*<m"]
81+
|===
82+
| name | isManager
83+
84+
| "Alice" | true
85+
| "Cecil" | false
86+
| "Cecilia" | false
87+
| "Charlie" | false
88+
| "Daniel" | true
89+
| "Eskil" | true
90+
91+
2+d|Rows: 6
92+
|===
93+
94+
`p:!CEO` tests whether `p` does not have the label `CEO`.
95+
96+
[source, cypher]
97+
----
98+
MATCH (p:Person)-[r]->(m)
99+
RETURN p.name AS name,
100+
m:!CEO AS doesNotWorkDirectlyForTheCEO
101+
----
102+
103+
[role="queryresult",options="header,footer",cols="2*<m"]
104+
|===
105+
| name | doesNotWorkDirectlyForTheCEO
106+
107+
| "Alice" | true
108+
| "Cecil" | true
109+
| "Cecilia" | true
110+
| "Charlie" | true
111+
| "Daniel" | false
112+
113+
2+d|Rows: 5
114+
|===
115+
116+
When '<exp>', then the label expression predicate results in `null`, e.g. if `p` is `null`, then `p:!CEO` results in `null`.
117+
118+
[source, cypher]
119+
----
120+
MATCH (p:Person)
121+
OPTIONAL MATCH (p)-[r]->(m)
122+
RETURN p.name AS name,
123+
m:!CEO AS doesNotWorkDirectlyForTheCEO
124+
----
125+
126+
[role="queryresult",options="header,footer",cols="2*<m"]
127+
|===
128+
| name | doesNotWorkDirectlyForTheCEO
129+
130+
| "Alice" | true
131+
| "Cecil" | true
132+
| "Cecilia" | true
133+
| "Charlie" | true
134+
| "Daniel" | false
135+
| "Eskil" | null
136+
137+
2+d|Rows: 6
138+
|===
139+
140+
The function xref:functions/scalar.adoc#functions-coalesce[coalesce] allows to turn a `null` into a default values:
141+
142+
[source, cypher]
143+
----
144+
MATCH (p:Person)
145+
OPTIONAL MATCH (p)-[r]->(m)
146+
RETURN p.name AS name,
147+
coalesce(m:!CEO, false) AS doesNotWorkDirectlyForTheCEO
148+
----
149+
150+
[role="queryresult",options="header,footer",cols="2*<m"]
151+
|===
152+
| name | doesNotWorkDirectlyForTheCEO
153+
154+
| "Alice" | true
155+
| "Cecil" | true
156+
| "Cecilia" | true
157+
| "Charlie" | true
158+
| "Daniel" | false
159+
| "Eskil" | false
160+
161+
2+d|Rows: 6
162+
|===
163+
164+
[role=label--new-Neo4j-2025.07 label--cypher-25-only]
165+
[[dynamic-node-label-expression-predicate]]
166+
== Test that node matches dynamic label expression
167+
168+
If we do not know the manager labels, we can infer a list of `managerLabels` by collecting all non-`Person` labels from people with a direct report.
169+
Nodes can be tested with the label expression predicate to match xref:patterns/reference.adoc#dynamic-label-and-type-expressions[dynamic label expression].
170+
`p:$any(managerLabels)` test whether node `p` has at least on of the label in the list `managerLabels`.
171+
172+
[source, cypher]
173+
----
174+
CYPHER 25
175+
MATCH ()-[r]->(p:Person)
176+
UNWIND labels(p) AS label
177+
FILTER label <> "Person"
178+
RETURN COLLECT(label) AS managerLabels
179+
NEXT
180+
MATCH (p)
181+
RETURN p.name AS name, p:$any(managerLabels) AS isManager
182+
----
183+
184+
[role="queryresult",options="header,footer",cols="2*<m"]
185+
|===
186+
| name | isManager
187+
188+
| "Alice" | true
189+
| "Cecil" | false
190+
| "Cecilia" | false
191+
| "Charlie" | false
192+
| "Daniel" | true
193+
| "Eskil" | true
194+
195+
2+d|Rows: 6
196+
|===
197+
198+
[node-label-expression-predicate]
199+
== Test that relationship has a certain relationship type
200+
201+
Given `r` is a relationship, `r:WORKS_FOR` tests whether `r` has the relationship type `WORKS_FOR` or not and result in `true` or `false`, respectively.
202+
203+
// tag::relationship-label-expression-predicate[]
204+
[source, cypher]
205+
----
206+
MATCH (p:Person)-[r]->()
207+
RETURN p.name AS name,
208+
r:WORKS_FOR AS isNotManager
209+
----
210+
// end::relationship-label-expression-predicate[]
211+
212+
[role="queryresult",options="header,footer",cols="2*<m"]
213+
|===
214+
| name | isNotManager
215+
216+
| "Alice" | false
217+
| "Cecil" | true
218+
| "Cecilia" | true
219+
| "Charlie" | true
220+
| "Daniel" | false
221+
222+
2+d|Rows: 5
223+
|===
224+
225+
If `r` is `null`, then the label expression predicate, e.g. `r:WORKS_FOR|REPORTS_TO` results in `null`.
226+
227+
[source, cypher]
228+
----
229+
MATCH (p:Person)
230+
OPTIONAL MATCH ()-[r]->(p)
231+
RETURN DISTINCT
232+
p.name AS name,
233+
coalesce(r:WORKS_FOR|REPORTS_TO, false) AS hasReports
234+
----
235+
236+
[role="queryresult",options="header,footer",cols="2*<m"]
237+
|===
238+
| name | hasReports
239+
240+
| "Alice" | true
241+
| "Cecil" | false
242+
| "Cecilia" | false
243+
| "Charlie" | false
244+
| "Daniel" | true
245+
| "Eskil" | true
246+
247+
2+d|Rows: 6
248+
|===
249+

modules/ROOT/pages/expressions/predicates/path-pattern-expressions.adoc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,59 @@ RETURN exists((:Person)-[:WORKS_FOR {since: 2023}]->(:Person {name: "Alice"})) A
127127

128128
1+d|Rows: 1
129129
|===
130+
131+
[[not-patterns]]
132+
== Not a path pattern expression
133+
134+
Since any expression can be surrounded by parentheses, some expression when parenthesized may be confused for being a path pattern expression.
135+
The following lists two examples of parenthesized expression that are not path pattern expression and explains what they are instead.
136+
137+
`(p:Person)` is not a path pattern expression since it does not have at least one xref::patterns/reference.adoc#relationship-patterns[relationship] or xref::patterns/reference.adoc#variable-length-relationships[variable-length relationship].
138+
Instead, `(p:Person)` is the xref:expressions/predicates/label-expression-predicates.adoc[label expression predicate] `p:Person` in parentheses.
139+
140+
.Parenthesized label expression predicate
141+
[source, cypher]
142+
----
143+
MATCH (employee:Person)-[:WORKS_FOR]->(p)
144+
WHERE (p:Person)
145+
RETURN employee.name AS employee, (p:Person) AS workForAPeron
146+
----
147+
148+
The above query has the same result as teh query that used `p:Person` in stead of `(p:Person)`:
149+
[source, cypher]
150+
----
151+
MATCH (employee:Person)-[:WORKS_FOR]->(p)
152+
WHERE p:Person
153+
RETURN employee.name AS employee, p:Person AS workForAPeron
154+
----
155+
156+
.Result
157+
[role="queryresult",options="header,footer",cols="2*<m"]
158+
|===
159+
| employee | workForAPeron
160+
161+
| "Cecil" | true
162+
| "Cecilia" | true
163+
164+
2+d|Rows: 2
165+
|===
166+
167+
`(p)` is not a path pattern expression since it does not have at least one xref::patterns/reference.adoc#relationship-patterns[relationship] or xref::patterns/reference.adoc#variable-length-relationships[variable-length relationship].
168+
Instead, `(p)` is the variable `p` in parentheses.
169+
170+
`(p)` does result in the value of variable `p`.
171+
If `p` is not a Boolean, then `(p)` does not result in a Boolean either.
172+
Hence, the following query throws an error:
173+
174+
.Parenthesized node variable
175+
[source, cypher, role=test-fail]
176+
----
177+
MATCH (employee:Person)-[:WORKS_FOR]->(p)
178+
WHERE (p)
179+
RETURN employee.name AS employee
180+
----
181+
182+
[source, error]
183+
----
184+
Invalid input 'Node' for `p`. Expected to be Boolean.
185+
----

0 commit comments

Comments
 (0)