Cypher is a query language for property graphs, originally created for the Neo4j graph database and later developed into an open standard as openCypher. Besides Neo4j, Cypher is supported by Memgraph, KùzuDB, Amazon Neptune, Apache AGE (with storage in PostgreSQL), and others. This language is named after a character in the movie The Matrix and is not related to ciphers in cryptography.
Example 3-4 shows the Cypher query to insert the lefthand portion of Figure 3-6 into a graph database. The rest of the graph can be added similarly. Each vertex is given a symbolic name, like usa or idaho. That name is not stored in the database but used only internally within the query to create edges between the vertices, using an arrow notation: (idaho) -[:WITHIN]-> (usa) creates an edge labeled WITHIN, with idaho as the tail node and usa as the head node.
Example 3-4. A subset of the data in Figure 3-6, represented as a Cypher query
CREATE
(namerica :Location {name:'North America', type:'continent'}),
(usa :Location {name:'United States', type:'country' }),
(idaho :Location {name:'Idaho', type:'state' }),
(lucy :Person {name:'Lucy' }),
(idaho) -[:WITHIN ]-> (usa) -[:WITHIN]-> (namerica),
(lucy) -[:BORN_IN]-> (idaho)
When all the vertices and edges of Figure 3-6 are added to the database, we can start asking interesting questions. For example, suppose we want to find the names of all the people who emigrated from the United States to Europe. We can do that by finding all the vertices that have a BORN_IN edge to a location within the US and a LIVING_IN edge to a location within Europe, and returning the name property of each of those vertices.
Example 3-5 shows how to express that query in Cypher. The same arrow notation is used in a MATCH clause to find patterns in the graph: (person) -[:BORN_IN]-> () matches any two vertices that are related by an edge labeled BORN_IN. The tail vertex of that edge is bound to the variable person, and the head vertex is left unnamed.
Example 3-5. A Cypher query to find people who emigrated from the US to Europe
MATCH
(person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (:Location {name:'United States'}),
(person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (:Location {name:'Europe'})
RETURN person.name
The query can be read as follows:
-
Find any vertex (call it person) that meets both of the following conditions:
-
person has an outgoing BORN_IN edge to a vertex. From that vertex, you can follow a chain of outgoing WITHIN edges until eventually you reach a vertex of type Location, whose name property is equal to United States.
-
That same person vertex also has an outgoing LIVES_IN edge. Following that edge, and then a chain of outgoing WITHIN edges, you eventually reach a vertex of type Location, whose name property is equal to Europe.
-
For each such person vertex, return the name property.
There are several possible ways of executing the query. The description given here suggests that you start by scanning all the people in the database, examining each person’s birthplace and residence, and returning only those people who meet the criteria.
But equivalently, you could start with the two Location vertices and work backward. If there is an index on the name property, you can efficiently find the two vertices representing the US and Europe. Then you can proceed to find all locations (states, regions, cities, etc.) in the US and Europe, respectively, by following all incoming WITHIN edges. Finally, you can look for people who can be found through an incoming BORN_IN or LIVES_IN edge at one of the location vertices.