In the property graph model, data is represented using graphs with nodes and edges that both can have types (called labels) and can be associated with a map of name-value pairs (called properties). In this demo, we will look at Cypher, a declarative query and data definition language for property graphs. We will use Neo4j which implements this language.
MATCH (n)
DETACH DELETE n
- the
CREATE
statement creates new nodes and edges. A single create statement can create multiple nodes and edges. The statement is finished with;
.
The syntax for creating a node is:
CREATE (nodeid:label { propertynameOne:'value1', ..., propertynameN:'valueN' });
Note that node ids are only valid within the scope of a single statement.
To create an edge:
CREATE (nodeidOne)-[:EDGE_LABEL] { propertynameOne:'value1', ..., propertynameN:'valueN' }->(nodeidTwo)
CREATE (matrix1:Movie { title : 'The Matrix', year : '1999-03-31' })
CREATE (matrix2:Movie { title : 'The Matrix Reloaded', year : '2003-05-07' })
CREATE (matrix3:Movie { title : 'The Matrix Revolutions', year : '2003-10-27' })
CREATE (keanu:Actor { name:'Keanu Reeves' })
CREATE (laurence:Actor { name:'Laurence Fishburne' })
CREATE (carrieanne:Actor { name:'Carrie-Anne Moss' })
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix1)
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix2)
CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix3)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix1)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix2)
CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix3)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix1)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix2)
CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix3)
We do not discuss this here. Have a look at https://neo4j.com/docs/cypher-manual/current/clauses/delete/ https://neo4j.com/docs/cypher-manual/current/clauses/set/.
- cypher is based on patterns that are matched against the graph specified in the
MATCH
clause - as side effects of matching patterns, variables specified in the pattern are bound to the nodes / edges matched against the pattern
- the
RETURN
clause specified what should be returned- Cypher queries either return a table or a graph depending on what is specified in the
RETURN
clause - as in SQL
*
returns everything
- Cypher queries either return a table or a graph depending on what is specified in the
A node pattern is specified as:
(variablename:label { propertynameOne:'value1', ..., propertynameN:'valueN' })
This pattern will match all nodes of type label
that have a property propertynameOne
with value value1
, a property propertynameTwo
with value value2
, and so one. All elements are optional, so the most basic node pattern that matches any node in the database is:
()
Consider some more examples:
(x) // match any node and bind it to variable x
(x:LABEL) // match any node of type LABEL and bind it to variable x
(:LABEL) // match any node of type LABEL, but do not bind to to any variable
(x { name: 'XXX' }) // match any node that has a property name with value XXX and bind it to variable x
Note that a pattern can consist of multiple parts
(x:Movie), (y:Actor) // bind x to all movies and y to all actors
Such a pattern defines constraints on an edge and its end points. The general form is ()-[]->()
where ()
are node patterns as described above and []
places restrictions on the edge and possibly binds it to a variable. For example, to find actors that acted in movies:
(a:Actor)-[r:ACTS_IN]->(movie:Movie)
To find actors that are playing role Neo
in some movie:
(a:Actor)-[r:ACTS_IN { role="Neo" }]->(movie:Movie)
Note that the edges can be written in either direction:
(movie:Movie)<-[r:ACTS_IN { role="Neo" }]-(a:Actor)
It is also possible to match on multiple relationship types:
(movie:Movie)<-[r:ACTS_IN|DIRECTS_IN]-(a:Actor)
Patterns can also encode paths of arbitrary length. Using *
paths of any length are matched. For example, to find movies that are connected somehow in the graph:
(movie:Movie)-[*]-(otherMovie:Movie)
It is also possible to specify that the path should be of a certain length or that the length of the path is between a lower and an upper bound.
(movie:Movie)-[*2]-(otherMovie:Movie) // movies connected to other movies with path of length 2
(movie:Movie)-[*1..4]-(otherMovie:Movie) // movies connected to other movies with paths of length 1 to 4
Other constraints on edges can be combined with path restrictions. For example, to movies that are direct or indirect sequels of another movie:
(movie:Movie)-[:SEQUEL_OF*]-(:Movie)
Paths can also be bound to variables like so:
p = (movie:Movie)-[:SEQUEL_OF*]-(:Movie)
Paths can also be bound to variables like so:
Returning *
returns the subgraph consisting of all nodes and edges that are matched by the pattern declared in the MATCH
clause.
For example, to return all actors and the movies they acted in:
MATCH (actor)-[r:ACTS_IN]->(movie)
RETURN *
To return all actors and movies in the database:
MATCH (actor:Actor),
(movie:Movie)
RETURN *
To return actors that played character Neo in
MATCH (a:Actor)-[r:ACTS_IN { role: "Neo" }]->(movie:Movie)
RETURN *
MATCH p = (a:Actor { name: "Laurence Fishburne" })-[*2]-(b:Actor)
RETURN *
- when only properties are selected in the
RETURN
clause then the result is a table - Let us return the name of actors that acted in some movie
match (actor)-[r:ACTS_IN]->(movie)
return actor.name
actor.name |
---|
Keanu Reeves |
Keanu Reeves |
Keanu Reeves |
Laurence Fishburne |
Laurence Fishburne |
Laurence Fishburne |
Carrie-Anne Moss |
Carrie-Anne Moss |
Carrie-Anne Moss |
- or the names of actors and the titles of movies they starred in and what their role was
MATCH (actor)-[r:ACTS_IN]->(movie)
RETURN actor.name, r.role, movie.title
actor.name | r.role | movie.title |
---|---|---|
Keanu Reeves | Neo | The Matrix Revolutions |
Keanu Reeves | Neo | The Matrix Reloaded |
Keanu Reeves | Neo | The Matrix |
Laurence Fishburne | Morpheus | The Matrix Revolutions |
Laurence Fishburne | Morpheus | The Matrix Reloaded |
Laurence Fishburne | Morpheus | The Matrix |
Carrie-Anne Moss | Trinity | The Matrix Revolutions |
Carrie-Anne Moss | Trinity | The Matrix Reloaded |
Carrie-Anne Moss | Trinity | The Matrix |
Like in SQL you can filter results using WHERE
MATCH (actor)-[r:ACTS_IN]->(movie)
WHERE actor.name = 'Keanu Reeves'
RETURN actor.name
actor.name |
---|
Keanu Reeves |
Keanu Reeves |
Keanu Reeves |
Using LIMIT
you can restrict the number of answer that are returned.
MATCH (actor)-[r:ACTS_IN]->(movie)
WHERE actor.name = 'Keanu Reeves'
RETURN actor.name
LIMIT 1
actor.name |
---|
Keanu Reeves |
With ORDER BY
answer can be ordered.
MATCH (actor)-[r:ACTS_IN]->(movie)
RETURN actor.name
ORDER BY actor.name
actor.name |
---|
Carrie-Anne Moss |
Carrie-Anne Moss |
Carrie-Anne Moss |
Keanu Reeves |
Keanu Reeves |
Keanu Reeves |
Laurence Fishburne |
Laurence Fishburne |
Laurence Fishburne |
With SKIP
you can skip n
results.
MATCH (actor)-[r:ACTS_IN]->(movie)
RETURN actor.name
ORDER BY actor.name
SKIP 3
LIMIT 3
actor.name |
---|
Keanu Reeves |
Keanu Reeves |
Keanu Reeves |
MATCH (n:Actor)
RETURN n.name AS name
UNION ALL
MATCH (n:Movie)
RETURN n.title AS name
name |
---|
Keanu Reeves |
Laurence Fishburne |
Carrie-Anne Moss |
The Matrix |
The Matrix Reloaded |
The Matrix Revolutions |
We can apply aggregation functions in the RETURN
clause
MATCH (n:Actor)
RETURN count(*)
count(*) |
---|
3 |
If the RETURN
clause also contains non-aggregation functions, then these are consider to be group-by expressions.
MATCH (a:Actor)-[:ACTS_IN]->(:Movie)
RETURN a.name, count(*)
a.name | count(*) |
---|---|
Keanu Reeves | 3 |
Laurence Fishburne | 3 |
Carrie-Anne Moss | 3 |
The WITH
clause allows an intermediate result to be specified that can then be fed into additional clauses, e.g., filtering after an aggregation as shown below. For more details see here.
MATCH (a:Actor)-[:ACTS_IN]->(:Movie)
WITH a, count(*) AS c
WHERE c > 2
RETURN a.name, c
a.name | c |
---|---|
Keanu Reeves | 3 |
Laurence Fishburne | 3 |
Carrie-Anne Moss | 3 |