Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/ROOT/images/graph_match_clause.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 87 additions & 0 deletions modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,93 @@ Either the pattern already exists, or it needs to be created.
| Syntactic construct for creating a `LIST` based on matchings of a pattern.
|===

[[dynamic-queries]]
== Dynamic queries

Node labels, relationship types, properties, and CSV columns can be referenced dynamically using Cypher.
This allows for more flexible queries and mitigates the risk of Cypher injection.
(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]).

[options="header", cols="2a,5a"]
|===
| Cypher feature
| Description

a|
[source, cypher, role="noheader"]
----
MATCH (n:$($label)),
()-[r:$($type))]->()
----

| xref:clauses/match.adoc#dynamic-match[`MATCH` nodes and relationships using dynamic node labels and relationship types]

a|
[source, cypher, role="noheader"]
----
CREATE (n:$($label)),
()-[r:$($type)]->()
----

| xref:clauses/create.adoc#dynamic-create[`CREATE` nodes and relationships using dynamic node labels and relationship types]

a|
[source, cypher, role="noheader"]
----
MERGE (n:$($label)),
()-[r:$($type)]->()
----

| xref:clauses/merge.adoc#dynamic-merge[`MERGE` nodes and relationships using dynamic node labels and relationship types]

a|
[source, cypher, role="noheader"]
----
LOAD CSV WITH HEADERS FROM 'file:///artists-with-headers.csv' AS line
CREATE (n:$(line.label) {name: line.Name})
----

| xref:clauses/load-csv.adoc#dynamic-columns[Import CSV files using dynamic columns]


a|
[source, cypher, role="noheader"]
----
MATCH (n)
SET n[$key] = value
----

| xref:clauses/set.adoc#dynamic-set-property[Dynamically `SET` or update a property]

a|
[source, cypher, role="noheader"]
----
MATCH (n:Label)
SET n:$(n.property)
----

| xref:clauses/set.adoc#dynamic-set-node-label[Dynamically `SET` a node label]

a|
[source, cypher, role="noheader"]
----
MATCH (n {name: 'Peter'})
REMOVE n:$($label)
----

| xref:clauses/remove.adoc#dynamic-remove-property[Dynamically `REMOVE` a property]

a|
[source, cypher, role="noheader"]
----
MATCH (n {name: 'Peter'})
REMOVE n:$($label)
----

| xref:clauses/remove.adoc#dynamic-remove-node-label[Dynamically `REMOVE` a node label]

|===


[[functions]]
== Functions
Expand Down
53 changes: 52 additions & 1 deletion modules/ROOT/pages/clauses/create.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,61 @@ Nodes created: 2 +
Properties set: 4
|===

[role=label--new-5.26]
[[dynamic-create]]
== CREATE nodes and relationships using dynamic node labels and relationship types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really long title, is it possible to make it shorter?

CREATE using dynamic node labels and relationship types perhaps?


Node labels and relationship types can be referenced dynamically in expressions, parameters, and variables when creating nodes and relationships.
This allows for more flexible queries and mitigates the risk of Cypher injection.
(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]).

.Syntax for creating nodes and relationships dynamically
[source, syntax]
----
CREATE (n:$(<expr>))
CREATE ()-[r:$(<expr>)]->()
----

The expression must evaluate to a `STRING NOT NULL | LIST<STRING NOT NULL> NOT NULL` value.
Using a `LIST<STRING>` with more than one item when creating a relationship using dynamic relationship types will fail.
This is because a relationship can only have exactly one type.

.Parameters
[source, parameters]
----
{
"nodeLabels": ["Person", "Director"],
"relType": "DIRECTED",
"movies": ["Ladybird", "Little Women", "Barbie"]
}
----

.Create nodes and relationships using dynamic node labels and relationship types
[source, cypher]
----
CREATE (greta:$($nodeLabels) {name: 'Greta Gerwig'})
WITH greta
UNWIND $movies AS movieTitle
CREATE (greta)-[rel:$($relType)]->(m:Movie {title: movieTitle})
RETURN greta.name AS name, labels(greta) AS labels, type(rel) AS relType, collect(m.title) AS movies
----

.Result
[role="queryresult",options="footer",cols="4*<m"]
|===
| name | labels | relType | movies

| "Greta Gerwig"
| ["Person", "Director"]
| "DIRECTED"
| ["Ladybird", "Little Women", "Barbie"]
4+d|Rows: 1 +
|===

[role=label--new-5.18]
[[insert-as-synonym-of-create]]
== `INSERT` as a synonym of `CREATE`

`INSERT` can be used as a synonym to `CREATE` for creating nodes and relationships, and was introduced as part of Cypher's xref:appendix/gql-conformance/index.adoc[].
However, `INSERT` requires that multiple labels are separated by an ampersand `&` and not by colon `:`.

Expand Down
40 changes: 40 additions & 0 deletions modules/ROOT/pages/clauses/load-csv.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,46 @@ Added 4 nodes, Set 8 properties, Added 4 labels
|===
====

[role=label--new-5.26]
[[dynamic-columns]]
=== Import CSV files using dynamic columns

CSV columns can be referenced dynamically to map labels to nodes in the graph.
This enables flexible data handling, allowing labels to be be populated from CSV column values without manually specifying each entry.
It also mitigates the risk of Cypher injection.
(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]).

.bands-with-headers.csv
[source, csv, filename="artists-with-headers.csv"]
----
Id,Label,Name
1,Band,The Beatles
2,Band,The Rolling Stones
3,Band,Pink Floyd
4,Band,Led Zeppelin
----

.Query
[source, cypher, role=test-skip]
----
LOAD CSV WITH HEADERS FROM 'file:///bands-with-headers.csv' AS line
MERGE (n:$(line.Label) {name: line.Name})
RETURN n AS bandNodes
----

.Result
[role="queryresult",options="header,footer",cols="1*<m"]
|===
| bandNodes

| (:Band {name: 'The Beatles'})
| (:Band {name: 'The Rolling Stones'})
| (:Band {name: 'Pink Floyd'})
| (:Band {name: 'Led Zeppelin'})

1+d|Rows: 4 +
Added 4 nodes, Set 4 properties, Added 4 labels
|===

=== Import compressed CSV files

Expand Down
137 changes: 130 additions & 7 deletions modules/ROOT/pages/clauses/match.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ To recreate the graph, run the following query against an empty Neo4j database:

[source, cypher, role=test-setup]
----
CREATE (charlie:Person {name: 'Charlie Sheen'}),
(martin:Person {name: 'Martin Sheen'}),
(michael:Person {name: 'Michael Douglas'}),
(oliver:Person {name: 'Oliver Stone'}),
(rob:Person {name: 'Rob Reiner'}),
CREATE (charlie:Person:Actor {name: 'Charlie Sheen'}),
(martin:Person:Actor {name: 'Martin Sheen'}),
(michael:Person:Actor {name: 'Michael Douglas'}),
(oliver:Person:Director {name: 'Oliver Stone'}),
(rob:Person:Director {name: 'Rob Reiner'}),
(wallStreet:Movie {title: 'Wall Street'}),
(charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet),
(martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet),
Expand Down Expand Up @@ -124,8 +124,10 @@ The above query uses the xref:functions/list.adoc#functions-labels[`labels()`] a
[role="queryresult",options="header,footer",cols="2*<m"]
|===
| label | labelCount
| ["Person"] | 5
2+d| Rows: 1
| ["Person", "Actor"] | 3
| ["Person", "Director"] | 2

2+d| Rows: 2
|===

For a list of all label expressions supported by Cypher, see xref:patterns/reference.adoc#label-expressions[Patterns -> Label expressions].
Expand Down Expand Up @@ -490,3 +492,124 @@ The above query uses the xref:functions/aggregating.adoc#functions-collect[`coll

For more information about how Cypher queries work, see xref:clauses/clause-composition.adoc[].

[role=label--new-5.26]
[[dynamic-match]]
== MATCH nodes and relationships using dynamic node labels and relationship types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here:

MATCH using dynamic node labels and relationship types


Node labels and relationship types can be referenced dynamically in expressions, parameters, and variables when matching nodes and relationships.
This allows for more flexible queries and mitigates the risk of Cypher injection.
(For more information about Cypher injection, see link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Neo4j Knowledge Base -> Protecting against Cypher injection]).

.Syntax for matching node labels dynamically
[source, syntax]
----
MATCH (n:$(<expr>))
MATCH (n:$any(<expr>))
MATCH (n:$all(<expr>))
----

[NOTE]
`MATCH (n:$all(<expr>))` is functionally equivalent to `MATCH (n:$(<expr>))`.

.Syntax for matching relationship types dynamically
[source, syntax]
----
MATCH ()-[r:$(<expr>))]->()
MATCH ()-[r:$any(<expr>)]->()
MATCH ()-[r:$all(<expr>))]->()
----

The expression must evaluate to a `STRING NOT NULL | LIST<STRING NOT NULL> NOT NULL` value.
If you use a `LIST<STRING>` with more than one item in a relationship pattern with dynamic relationship types, no results will be returned.
This is because a relationship can only have exactly one type.

.Match labels dynamically
[source, cypher]
----
WITH ["Person", "Director"] AS labels
MATCH (directors:$(labels))
RETURN directors
----

.Result
[role="queryresult",options="header,footer",cols="1*<m"]
|===
| directors

| (:Person:Director {name: "Oliver Stone"})
| (:Person:Director {name: "Rob Reiner"})

1+d|Rows: 2
|===

.Match nodes dynamically using the `any()` function
[source, cypher]
----
MATCH (n:$any(["Movie", "Actor"]))
RETURN n AS nodes
----

[NOTE]
The xref:functions/predicate.adoc#functions-any[`any()`] function matches nodes that have any of the specified labels.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like the any in the match is the any() function, but it isn't. It's behaviour is modeled off of the any function, but it is not identical, so maybe clarify this part a little more


.Result
[role="queryresult",options="header,footer",cols="1*<m"]
|===
| nodes

| (:Person:Actor {name: "Charlie Sheen"})
| (:Person:Actor {name: "Martin Sheen"})
| (:Person:Actor {name: "Michael Douglas"})
| (:Movie {title: "Wall Street"})
| (:Movie {title: "The American President"})

1+d|Rows: 5
|===


.Parameter
[source, parameters]
----
{
"label": "Movie"
}
----

.Match nodes dynamically using a parameter
[source, cypher]
----
MATCH (movie:$($label))
RETURN movie.title AS movieTitle
----

.Result
[role="queryresult",options="header,footer",cols="1*<m"]
|===
| movieTitle

| "Wall Street"
| "The American President"

1+d|Rows: 2
|===


.Match relationships dynamically using a variable
[source, cypher]
----
CALL db.relationshipTypes()
YIELD relationshipType
MATCH ()-[r:$(relationshipType)]->()
RETURN relationshipType, count(r) AS relationshipCount
----

.Result
[role="queryresult",options="header,footer",cols="2*<m"]
|===
| relationshipType | relationshipCount

| "ACTED_IN" | 5
| "DIRECTED" | 2

2+d|Rows: 2
|===
Loading