Patterns
Cypher® relies on pattern matching to find data.
Patterns are often used in MATCH
statements such as:
MATCH (m:Movie)<-[:ACTED_IN]-(a:Actor)
Patterns can be arbitrarily complex and @neo4j/cypher-builder
provides the necessary tools to define them.
Nodes and relationships
A pattern is formed by nodes and relationships. The first step is to define the variables that are referenced in the pattern:
const person = new Cypher.Node();
const movie = new Cypher.Node();
const actedIn = new Cypher.Relationship();
Basic patterns
All patterns begin and end with a node. By using the variables defined previously, the pattern can be defined as follows:
const pattern = new Cypher.Pattern(person, { labels: ["Person"] }).related(actedIn, { type: "ACTED_IN" }).to(movie, { labels: ["Movie"] });
This pattern can now be used anywhere where a pattern can be used, for example, in a MATCH
clause:
new Cypher.Match(pattern);
MATCH (this0:Person)-[this1:ACTED_IN]->(this2:Movie)
Relationship direction
By default, a relationship in a pattern is created as a left-to-right pattern.
The direction can be changed when defining the relationship in the pattern using the property direction
:
const pattern = new Cypher.Pattern(person)
.related(actedIn, { type: "ACTED_IN", direction: "left" })
.to(movie);
This translates to the following pattern:
(this0)<-[this1:ACTED_IN]-(this2)
The options for direction
are:
-
right
(default): a left-to-right (()-[]→()
) pattern. -
left
: a right-to-left (()←[]-()
) pattern. -
undirected
: an undirected (()-[]-()
) pattern.
Remove variable names
Variables for nodes and relationships are optional in the pattern, by not passing a variable it will not be rendered in Cypher:
const pattern = new Cypher.Pattern(person, { labels: ["Person"] }).related({type: "ACTED_IN"}).to({labels: ["Movie"]});
This translates to:
(this0:Person)-[:ACTED_IN]->(:Movie)
Remove labels and types
Labels and types are optional in the pattern:
const pattern = new Cypher.Pattern(person).related(actedIn).to(movie, { labels: ["Movie"] });
(this0)-[this1]->(this1:Movie)
Labels and types can be also defined in |
Properties
Patterns may contain properties to match both nodes and relationships.
These can be added using the properties
argument:
const pattern = new Cypher.Pattern(person, {
labels: ["Person"],
properties: { name: new Cypher.Param("Person") },
}).related(actedIn, { type: "ACTED_IN" })
.to(movie, { labels: ["Movie"] });
(this0:Person { name: $param0 })-[this1:ACTED_IN]->(this2:Movie)
The properties
argument takes an object with the properties to match and the param objects to be used as the expected values of the pattern.
It can be used in both node and relationship elements.
Advanced patterns
This section shows how to define more complex patterns.
Longer patterns
Patterns can be arbitrarily long. For example:
const user = new Cypher.Node();
const pattern = new Cypher.Pattern({labels: ["Person"]}).related({type: "ACTED_IN"}).to().related({direction: "left"}).to(user, { labels: ["User"] });
(:Person)-[:ACTED_IN]->()<-[]-(this1:User)
Cycles
A pattern may have cycles. To achieve this, you can reuse the same variables:
const actor = new Cypher.Node();
const movie = new Cypher.Node();
const actedIn = new Cypher.Relationship();
const directed = new Cypher.Relationship();
const pattern = new Cypher.Pattern(person, { labels: ["Person"] })
.related(actedIn, { type: "ACTED_IN" })
.to(movie, { labels: ["Movie"] })
.related(directed, { direction: "undirected", type: "DIRECTED" })
.to(actor);
This translates to:
(this0:Person)-[this1:ACTED_IN]->(this2:Movie)-[this3:DIRECTED]-(this0)
Note how the initial node in the pattern (this0
) is the same as the one referenced in the last element.
This matches actors who also directed the same movie.
Length
The length (or hops) of a relationship can be defined with the length
property.
Exact length
The exact length can be defined by passing a number:
const pattern = new Cypher.Pattern({}).related(actedIn, { type: "ACTED_IN", length: 3 }).to();
MATCH ()-[this1:ACTED_IN*3]->()
Min and max length
Bounds can be optinally added by passing an object with the following options:
-
min
: defines the minimum length of the relationship. -
max
: defines the maximum length of the relationship.
For example:
const pattern = new Cypher.Pattern({}).related(actedIn, { type: "ACTED_IN", length: {min: 2, max: 10} }).to();
MATCH ()-[this1:ACTED_IN*2..10]->()
WHERE
predicates
WHERE
clauses can be used as predicates for both nodes and relationships in the pattern:
const movie = new Cypher.Node({ labels: ["Movie"] });
new Cypher.Pattern(movie, { labels: ["Movie"] }).where(Cypher.eq(movie.property("title"), new Cypher.Literal("The Matrix")));
(this0:Movie WHERE this0.title = "The Matrix")
Escaping labels and types
Labels and types will be automatically escaped if any uncommon character is detected. For example:
const movie = new Cypher.Node();
const match = new Cypher.Match(movie, { labels: ["My Movie"] }).return(movie);
MATCH (this0:`My Movie`)
RETURN this0
Note that My Movie
is being surrounded in quotes to prevent code injection with dynamic labels.
Partial patterns
All patterns begin and end with a Node.
However, it is possible to define a partial pattern by using .related
without .to
:
const partialPattern = new Cypher.Pattern(person).related(actedIn);
In this case, the partial pattern cannot be used in any clause until it is completed with the .to
method:
partialPattern.to(movie)