Skip to content

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

puppy-cypher> :> UNWIND ['a', 'b', 'b'] as x RETURN x
==>[x:a]
==>[x:b]
==>[x:b]

DISTINCT

puppy-cypher> :> UNWIND ['a', 'b', 'b'] as x RETURN DISTINCT x
==>[x:a]
==>[x:b]

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.