Cypher Query Language
Cypher is a declarative query language specifically designed for interacting with graph databases in the Neo4j ecosystem. With its intuitive and expressive syntax, Cypher enables users to efficiently traverse and manipulate graph structures using a combination of pattern matching and graph traversal techniques, making it a popular choice for developers and data scientists working with graph-based data models.
PuppyGraph provides support for openCypher (version 9), an open-source implementation of Cypher.
Cypher Console
We will use the Cypher Console in the short tutorial.
Example Graph
Following the Launching PuppyGraph in Docker guide, we already setup a demo modern graph data with PuppyGraph that can be used for the rest of the document as an example. You may also use your own graph data.
Hello World
Here are some basic examples of Cypher queries:
MATCH (v) RETURN v
all vertexes.MATCH ()-[e]->() RETURN e
all edges.MATCH (v) RETURN count(v)
total number of vertexes.MATCH ()-[e]->() RETURN count(e)
total number of (directed) edges.MATCH ()-[e]-() RETURN count(e)
total number of edges while direction is ignored. An edge(a)-[e]->(b)
is counted twice as(a,b)
and(b,a)
.
puppy-cypher> :> MATCH (v) RETURN v
==>[v:[_type:node,name:vadas,_id:person:::v2,_label:person,age:27]]
==>[v:[_type:node,name:peter,_id:person:::v6,_label:person,age:35]]
==>[v:[_type:node,name:marko,_id:person:::v1,_label:person,age:29]]
==>[v:[_type:node,name:josh,_id:person:::v4,_label:person,age:32]]
==>[v:[_type:node,name:ripple,_id:software:::v5,lang:java,_label:software]]
==>[v:[_type:node,name:lop,_id:software:::v3,lang:java,_label:software]]
puppy-cypher> :> MATCH ()-[e]->() RETURN e
==>[e:[_type:relationship,_outV:person:::v1,weight:0.5,_id:knows:::e7,_inV:person:::v2,_label:knows]]
==>[e:[_type:relationship,_outV:person:::v1,weight:1.0,_id:knows:::e8,_inV:person:::v4,_label:knows]]
==>[e:[_type:relationship,_outV:person:::v1,weight:0.4,_id:created:::e9,_inV:software:::v3,_label:created]]
==>[e:[_type:relationship,_outV:person:::v4,weight:1.0,_id:created:::e10,_inV:software:::v5,_label:created]]
==>[e:[_type:relationship,_outV:person:::v4,weight:0.4,_id:created:::e11,_inV:software:::v3,_label:created]]
==>[e:[_type:relationship,_outV:person:::v6,weight:0.2,_id:created:::e12,_inV:software:::v3,_label:created]]
puppy-cypher> :> MATCH (v) RETURN count(v)
==>[count(v):6]
puppy-cypher> :> MATCH ()-[e]->() RETURN count(e)
==>[count(e):6]
puppy-cypher> :> MATCH ()-[e]-() RETURN count(e)
==>[count(e):12]
Basic Pattern Matching
Working with id
puppy-cypher> :> MATCH (v) RETURN id(v)
==>[id(v):person:::v2]
==>[id(v):person:::v4]
==>[id(v):person:::v6]
==>[id(v):person:::v1]
==>[id(v):software:::v3]
==>[id(v):software:::v5]
Patterns for labels
Show nodes with a certain label.
puppy-cypher> :> MATCH (v:person) RETURN v
==>[v:[_type:node,name:peter,_id:person:::v6,_label:person,age:35]]
==>[v:[_type:node,name:josh,_id:person:::v4,_label:person,age:32]]
==>[v:[_type:node,name:vadas,_id:person:::v2,_label:person,age:27]]
==>[v:[_type:node,name:marko,_id:person:::v1,_label:person,age:29]]
Specifying properties
Show nodes with a certain property.
puppy-cypher> :> MATCH (v {name: 'marko'}) RETURN v
==>[v:[_type:node,name:marko,_id:person:::v1,_label:person,age:29]]
Use where for more flexible filters
puppy-cypher> :> MATCH (v) WHERE v.age > 30 RETURN v
==>[v:[_type:node,name:josh,_id:person:::v4,_label:person,age:32]]
Output Control
Returning certain properties
puppy-cypher> :> MATCH (v) WHERE v.age > 30 RETURN v.name, v.age
==>[v.name:josh,v.age:32]
==>[v.name:peter,v.age:35]
Using lists
puppy-cypher> :> MATCH (v) WHERE v.name in ['marko', 'josh', 'peter'] AND v.age > 30 RETURN [v.name, v.age]
==>[[v.name, v.age]:[josh,32]]
==>[[v.name, v.age]:[peter,35]]
Unwinding a list
DISTINCT
SKIP and LIMIT
SKIP
is to skip the first several results, and LIMIT
is to limit the number of results.
puppy-cypher> :> MATCH (v:person) RETURN v SKIP 1 LIMIT 2
==>[v:[_type:node,name:marko,_id:person:::v1,_label:person,age:29]]
==>[v:[_type:node,name:peter,_id:person:::v6,_label:person,age:35]]
Showing paths
Assign the path to a variable.
puppy-cypher> :> MATCH p=(u:person)-->(v:person) RETURN p
==>[p:[_elements:[[_type:node,name:marko,_id:person:::v1,_label:person,age:29],[_type:relationship,weight:0.5,_outV:person:::v1,_id:knows:::e7,_label:knows,_inV:person:::v2],[_type:node,name:vadas,_id:person:::v2,_label:person,age:27]],_type:path]]
==>[p:[_elements:[[_type:node,name:marko,_id:person:::v1,_label:person,age:29],[_type:relationship,weight:1.0,_outV:person:::v1,_id:knows:::e8,_label:knows,_inV:person:::v4],[_type:node,name:josh,_id:person:::v4,_label:person,age:32]],_type:path]]
More complex queries
Searching for a Graph Pattern
Who are marko
's co-creators?
puppy-cypher> :> MATCH (u:person {name:'marko'})-[:created]->(:software)<-[:created]-(v:person) RETURN v.name
==>[v.name:josh]
==>[v.name:peter]
Note that in a pattern, an edge is only used once. So marko
is not his own co-creator. See Uniqueness section of openCypher (version 9) for more information.
Variable-length pattern matching
Here is another query to get marko
's co-creators. The direction of edges is ignored.
puppy-cypher> :> MATCH (u:person {name:'marko'})-[:created*2]-(v) RETURN v.name
==>[v.name:josh]
==>[v.name:peter]
Using WITH to pipe results
puppy-cypher> :> MATCH ({name: 'marko'})-[:knows]->(v) WITH v MATCH (v)-[:created]->(x) RETURN v.name AS otherPerson, x.name AS Software
==>[otherPerson:josh,Software:ripple]
==>[otherPerson:josh,Software:lop]
We also use AS
to rename the results here.
Official Specification
For detailed Cypher language specification and more resources, please refer to the openCypher official website.