-
Notifications
You must be signed in to change notification settings - Fork 63
Not a path pattern expression #1431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
8b4efc1
bbcc228
9ccc313
e486abe
ffe5362
9609711
4319092
d4012a3
7f87ba9
d7b9ad2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| = Label expression predicates | ||
| :description: This page describes how to use label expression predicates with Cypher. | ||
|
|
||
| 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. | ||
|
|
||
| [[label-expression-predicat-syntax]] | ||
| == Syntax | ||
|
|
||
| [source, syntax] | ||
| ---- | ||
| <expr>:<label-expression> | ||
| ---- | ||
|
|
||
| Where `<expr>` is any Cypher expression and `<label-expression>` is any Cypher xref::patterns/reference.adoc#label-expressions[label expression]. | ||
|
|
||
| [[example-graph]] | ||
| == Example graph | ||
|
|
||
| The following graph is used for the examples below: | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| image::label_expression_predicates_graph.svg[width="700",role="middle"] | ||
|
|
||
| To recreate the graph, run the following query against an empty Neo4j database: | ||
|
|
||
| [source, cypher] | ||
| ---- | ||
| CREATE (alice:Person&Manager {name:'Alice', age: 65, skills: ['Java', 'Python']}), | ||
| (cecil:Person&Developer {name: 'Cecil', age: 25, skills: ['Java', 'Python']}), | ||
| (cecilia:Person&Developer {name: 'Cecilia', age: 31, skills: ['JavaScript', 'TypeScript']}), | ||
| (charlie:Person&Engineer {name: 'Charlie', age: 61, skills: ['C++', 'Python']}), | ||
| (daniel:Person&Director {name: 'Daniel', age: 39, skills: ['JavaScript', 'Slides']}), | ||
| (eskil:Person&CEO {name: 'Eskil', age: 39, skills: ['Slides', 'ChatGPT']}), | ||
| (cecil)-[:WORKS_FOR]->(alice), | ||
| (cecilia)-[:WORKS_FOR]->(alice), | ||
| (charlie)-[:WORKS_FOR]->(daniel), | ||
| (alice)-[:REPORTS_TO]->(daniel), | ||
| (daniel)-[:REPORTS_TO]->(eskil) | ||
| ---- | ||
|
|
||
| [node-label-expression-predicate] | ||
| == Test that node has a certain labels | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Given `p` is a node, `p:Manager` tests whether node `p` has the label `Manager` or not and results in `true` or `false`, respectively. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // tag::node-label-expression-predicate-1[] | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person) | ||
| RETURN p.name AS name, p:Manager AS isManager | ||
| ---- | ||
| // end::node-label-expression-predicate-1[] | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | isManager | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | false | ||
| | "Cecilia" | false | ||
| | "Charlie" | false | ||
| | "Daniel" | false | ||
| | "Eskil" | false | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| 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. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| More specifically, `p:Manager|Director|CEO` tests whether `p` has at least one of the three labels `Manager`, `Director`, and `CEO`. | ||
|
|
||
| // tag::node-label-expression-predicate-2[] | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person) | ||
| RETURN p.name AS name, | ||
| p:Manager|Director|CEO AS isManager | ||
| ---- | ||
| // end::node-label-expression-predicate-2[] | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | isManager | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | false | ||
| | "Cecilia" | false | ||
| | "Charlie" | false | ||
| | "Daniel" | true | ||
| | "Eskil" | true | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| `p:!CEO` tests whether `p` does not have the label `CEO`. | ||
|
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person)-[r]->(m) | ||
| RETURN p.name AS name, | ||
| m:!CEO AS doesNotWorkDirectlyForTheCEO | ||
| ---- | ||
|
Comment on lines
+93
to
+97
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no tags for this node-label-expression-predicate?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never the remember the purpose of the tags. I thought they mark which examples go into the cheat sheet, so I picked just some of them. But maybe the cheat sheet things was somethings else. If all need a tag, then so be it. |
||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | doesNotWorkDirectlyForTheCEO | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | true | ||
| | "Cecilia" | true | ||
| | "Charlie" | true | ||
| | "Daniel" | false | ||
|
|
||
| 2+d|Rows: 5 | ||
| |=== | ||
|
|
||
| When `<exp>` results in ´null`, then the label expression predicate results in `null`, e.g. if `p` is `null`, then `p:!CEO` results in `null`. | ||
|
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person) | ||
| OPTIONAL MATCH (p)-[r]->(m) | ||
| RETURN p.name AS name, | ||
| m:!CEO AS doesNotWorkDirectlyForTheCEO | ||
| ---- | ||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | doesNotWorkDirectlyForTheCEO | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | true | ||
| | "Cecilia" | true | ||
| | "Charlie" | true | ||
| | "Daniel" | false | ||
| | "Eskil" | null | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| The function xref:functions/scalar.adoc#functions-coalesce[coalesce] allows to turn a `null` into a default values: | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person) | ||
| OPTIONAL MATCH (p)-[r]->(m) | ||
| RETURN p.name AS name, | ||
| coalesce(m:!CEO, false) AS doesNotWorkDirectlyForTheCEO | ||
| ---- | ||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | doesNotWorkDirectlyForTheCEO | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | true | ||
| | "Cecilia" | true | ||
| | "Charlie" | true | ||
| | "Daniel" | false | ||
| | "Eskil" | false | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| [role=label--new-Neo4j-2025.07 label--cypher-25-only] | ||
| [[dynamic-node-label-expression-predicate]] | ||
| == Test that node matches dynamic label expression | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Nodes can be tested with the label expression predicate to match xref:patterns/reference.adoc#dynamic-label-and-type-expressions[dynamic label expression]. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| `p:$any(managerLabels)` test whether node `p` has at least on of the label in the list `managerLabels`. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH ()-[r]->(p:Person) | ||
| UNWIND labels(p) AS label | ||
| FILTER label <> "Person" | ||
| RETURN COLLECT(label) AS managerLabels | ||
| NEXT | ||
| MATCH (p) | ||
| RETURN p.name AS name, p:$any(managerLabels) AS isManager | ||
| ---- | ||
|
Comment on lines
+169
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, no tags? (not sure how many we need for the cheat sheet 🤔 )
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I remembered correctly that they are for the cheat sheet. The ones with tags are my picks. But please, just take them as uninformed suggestions. I have no higher understanding of what typically is expected to go into the cheat sheet. I am just running on very basic subjective assumptions. Feel free to adjust that. |
||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | isManager | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | false | ||
| | "Cecilia" | false | ||
| | "Charlie" | false | ||
| | "Daniel" | true | ||
| | "Eskil" | true | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| [node-label-expression-predicate] | ||
| == Test that relationship has a certain relationship type | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // tag::relationship-label-expression-predicate[] | ||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person)-[r]->() | ||
| RETURN p.name AS name, | ||
| r:WORKS_FOR AS isNotManager | ||
| ---- | ||
| // end::relationship-label-expression-predicate[] | ||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | isNotManager | ||
|
|
||
| | "Alice" | false | ||
| | "Cecil" | true | ||
| | "Cecilia" | true | ||
| | "Charlie" | true | ||
| | "Daniel" | false | ||
|
|
||
| 2+d|Rows: 5 | ||
| |=== | ||
|
|
||
| If `r` is `null`, then the label expression predicate, e.g. `r:WORKS_FOR|REPORTS_TO` results in `null`. | ||
|
||
| The function xref:functions/scalar.adoc#functions-coalesce[coalesce] allows to turn a `null` into a default values. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (p:Person) | ||
| OPTIONAL MATCH ()-[r]->(p) | ||
| RETURN DISTINCT | ||
| p.name AS name, | ||
| coalesce(r:WORKS_FOR|REPORTS_TO, false) AS hasReports | ||
| ---- | ||
|
|
||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | name | hasReports | ||
|
|
||
| | "Alice" | true | ||
| | "Cecil" | false | ||
| | "Cecilia" | false | ||
| | "Charlie" | false | ||
| | "Daniel" | true | ||
| | "Eskil" | true | ||
|
|
||
| 2+d|Rows: 6 | ||
| |=== | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,3 +127,59 @@ RETURN exists((:Person)-[:WORKS_FOR {since: 2023}]->(:Person {name: "Alice"})) A | |
|
|
||
| 1+d|Rows: 1 | ||
| |=== | ||
|
|
||
| [[not-patterns]] | ||
| == Not a path pattern expression | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Since any expression can be surrounded by parentheses, some expression when parenthesized may be confused for being a path pattern expression. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| The following lists two examples of parenthesized expression that are not path pattern expression and explains what they are instead. | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| `(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]. | ||
| Instead, `(p:Person)` is the xref:expressions/predicates/label-expression-predicates.adoc[label expression predicate] `p:Person` in parentheses. | ||
|
|
||
| .Parenthesized label expression predicate | ||
| [source, cypher] | ||
| ---- | ||
| MATCH (employee:Person)-[:WORKS_FOR]->(p) | ||
| WHERE (p:Person) | ||
| RETURN employee.name AS employee, (p:Person) AS workForAPeron | ||
| ---- | ||
|
|
||
| The above query has the same result as the query that used `p:Person` instead of `(p:Person)`: | ||
hvub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [source, cypher] | ||
| ---- | ||
| MATCH (employee:Person)-[:WORKS_FOR]->(p) | ||
| WHERE p:Person | ||
| RETURN employee.name AS employee, p:Person AS workForAPerson | ||
| ---- | ||
|
|
||
| .Result | ||
| [role="queryresult",options="header,footer",cols="2*<m"] | ||
| |=== | ||
| | employee | workForAPerson | ||
|
|
||
| | "Cecil" | true | ||
| | "Cecilia" | true | ||
|
|
||
| 2+d|Rows: 2 | ||
| |=== | ||
|
|
||
| `(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]. | ||
| Instead, `(p)` is the variable `p` in parentheses. | ||
|
|
||
| `(p)` does result in the value of variable `p`. | ||
| If `p` is not a Boolean, then `(p)` does not result in a Boolean either. | ||
| Hence, the following query throws an error: | ||
|
|
||
| .Parenthesized node variable | ||
| [source, cypher, role=test-fail] | ||
| ---- | ||
| MATCH (employee:Person)-[:WORKS_FOR]->(p) | ||
| WHERE (p) | ||
| RETURN employee.name AS employee | ||
| ---- | ||
|
|
||
| [source, error] | ||
| ---- | ||
| Invalid input 'Node' for `p`. Expected to be Boolean. | ||
| ---- | ||
|
Comment on lines
+182
to
+185
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessary for this PR, but we should maybe think about starting to use the GQL errors in the Cypher manual instead.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean the showing the code? — because the message is what I get on 2025.10.1, i.e. it is the message that comes with the code. I guess that is a topic for the error team sync. It should have PM involvement, considering how error should be shown in docs (e.g. with position and carrot) and be done as a concerted change to the whole docs.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, showing the code and status description rather than or additional to the message. I will write it up for an upcoming error meeting, I agree it does not make much sense to do it for one random example but it should be consistently applied
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added it like this to next meeting:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Linking to the status code doc would be very reasonable. The docs colleagues maybe can even come up if a macro so that in the ascii doc sources you only have put the marco with the status code and it will generate a nice up-to-date presentation of the error incl. link to the respective status code doc page. |
||
Uh oh!
There was an error while loading. Please reload this page.