{{announcement.body}}
{{announcement.title}}

Graph Query Language Comparison - Gremlin vs. Cypher vs. nGQL

DZone 's Guide to

Graph Query Language Comparison - Gremlin vs. Cypher vs. nGQL

Graph users have to learn different graph query languages using different graph databases due to the lack of international standard of the graph query langua...

· Database Zone ·
Free Resource

In September 2019, Graph Query Language is accepted as a new database query language standard in a vote by the International SQL Standards Committee, the unification of GQL takes time.

GraphQL dialect comparison

In this post, we've selected some mainstream graph query languages and compared the CRUD usage in these languages respectively.

Which Graph Query Languages to Be Compared

GraphQL

Gremlin

Gremlin is a graph traversal language developed by Apache TinkerPop and has been adopted by a lot of graph database solutions. It can be either declarative or imperative.

Gremlin is Groovy-based, but has many language variants that allow developers to write Gremlin queries natively in many modern programming languages such as Java, JavaScript, Python, Scala, Clojure and Groovy.

Supported graph databases: Janus Graph, InfiniteGraph, Cosmos DB, DataStax Enterprise(5.0+) and Amazon Neptune.

Cypher

Cypher is a declarative graph query language that allows expressive and efficient data querying in a property graph.

The language was designed with the power and capability of SQL. The keywords of the Cypher language are not case sensitive, but attributes, labels, relationship types and variables are case sensitive.

Supported graph databases: Neo4j, AgensGraph and RedisGraph

nGQL

Nebula Graph introduces its own query language, nGQL, which is a declarative, textual query language like SQL, but designed for graphs.

The keywords of the nGQL language are case sensitive and it support statement composition so that there's no need for statement embedding.

Support graph databases: Nebula Graph

Terms Comparison

Before comparing the three graph query languages, let's take a look at their terms and concepts first. The table below explains how these languages define nodes and edges:

Graph Terms Comparison among Cypher, Gremlin, and nGQL

Syntax Comparison - CRUD

After understanding the common terms in Gremlin, Cypher, and nGQL, let's take a look at the general syntax of these graph query languages.

This section will walk you through the basic CRUD syntax for Gremlin, Cypher and nGQL respectively.

Graph

Refer to the following example on how to create a graph space. We omitted Cypher since you don't need to create a graph space before you can add any data to the graph databases.

Cypher
 




xxxxxxxxxx
1


 
1
# Create a graph that Gremlin can traverse 
2
g = TinkerGraph.open().traversal() 
3
 
            
4
# Create a graph space in nGQL 
5
 
            
6
CREATE SPACE gods 


Vertex

We all know that graph consists of nodes and edges. A node in Cypher is called a vertex in Gremlin and nGQL. And an edge is the connection among two nodes.

Refer to the following example on inserting new vertex in these query languages respectively.

Cypher
 




xxxxxxxxxx
1


 
1
# Insert vertex in Gremlin
2
g.addV(vertexLabel).property()
3
 
           
4
# Insert vertex in Cypher
5
CREATE (:nodeLabel {property})
6
 
           
7
# Insert vertex in nGQL
8
INSERT VERTEX tagName (propNameList) VALUES vid:(tagKey propValue)


Vertex Type

The nodes/vertices can have types. They are called labels in Gremlin and Cypher , and tags in nGQL.

A vertex type can have multiple properties. For example, vertex type Person have two properties, i.e. name and age.

Node relationships
Create Vertex Type

Refer to the following example on vertex type creation. We omitted Cypher since you don't need a label before inserting data.

Cypher
 




xxxxxxxxxx
1


 
1
# Create vertex type in Gremlin 
2
g.addV(vertexLabel).property() 
3
 
            
4
# Create vertex type in nGQL 
5
CREATE tagName(PropNameList) 



Note that Gremlin and nGQL both support IF NOT EXISTS. This keyword automatically detects if the corresponding vertex type exists. If it does not exist, a new one is created. Otherwise, no vertex type is created.

Show Vertex Types

When the vertex types are created, you can show them with the following queries. They will list all the labels/tags rather than certain labels/tags.

Cypher
 




xxxxxxxxxx
1
11


 
1
# Show vertex types in Gremlin
2
g.V().label().dedup();
3
 
           
4
# Show vertex types in Cypher method 1
5
MATCH (n) 
6
RETURN DISTINCT labels(n)
7
# Show vertex types in Cypher method 2
8
CALL db.labels();
9
 
           
10
# Show vertex types in nGQL
11
SHOW TAGS



CRUD on Vertices

This section introduces the basic CRUD operations on vertices using the three query languages.

Get Vertices
Cypher
 




xxxxxxxxxx
1
10


 
1
# Fetch vertices in Gremlin 
2
g.V(<vid>) 
3
 
            
4
# Fetch vertices in Cypher 
5
MATCH (n) 
6
WHERE condition 
7
RETURN properties(n) 
8
 
            
9
# Fetch vertices in nGQL 
10
FETCH PROP ON <tag_name> <vid



Delete Vertices
Cypher
 




xxxxxxxxxx
1


 
1
# Delete vertex in Gremlin 
2
g.V(<vid>).drop() 
3
 
            
4
# Delete a vertex in Cypher 
5
MATCH (node:label) 
6
DETACH DELETE node 
7
 
            
8
# Delete vertex in nGQL 
9
DELETE VERTEX <vid



This section shows you how to update properties for vertex.

Cypher
 




xxxxxxxxxx
1


 
1
# Update vertex in Gremlin 
2
g.V(<vid>).property() 
3
 
            
4
# Update vertex in Cypher 
5
SET n.prop = V 
6
 
            
7
# Update vertex in nGQL 
8
UPDATE VERTEX <vid> SET <update_columns



Both Cypher and nGQL use keyword SET to set the vertex type, except that the UPDATE keyword is added in nGQL to identify the operation. The operation of Gremlin is similar to the above-mentioned fetching vertex, except that it adds operations for changing property.

Edges

This section introduces the basic CRUD operations on edges.

Edge Types

Like vertices, edges can also have types.

Cypher
 




xxxxxxxxxx
1


 
1
# Create an edge type in Gremlin 
2
g.edgeLabel() 
3
 
            
4
# Create an edge type in nGQL 
5
CREATE EDGE edgeTypeName(propNameList) 



CRUD on Edges

Insert Edges of Certain Types

Inserting an edge is similar to inserting vertices. Cypher uses -[]-> and and nGQL uses -> to represent edges respectively. Gremlin uses the keyword to() to indicate edge direction.

Edges are by default directed in the three languages. The chart on the left below is a directed edge while the one on the right is an undirected edge.

Graph database relationships

Delete Edges

Cypher
 




xxxxxxxxxx
1


 
1
# Delete edge in Gremlin 
2
g.E(<eid>).drop() 
3
 
            
4
# Delete edge in Cypher 
5
MATCH (<node1-name>:<label1-name>)-[r:relationship-label-name]->() 
6
DELETE r 
7
 
            
8
# Delete edge in nGQL 
9
DELETE EDGE <edge_type> <src_vid> -> <dst_vid



Fetch Edges

Cypher
 




xxxxxxxxxx
1
10


 
1
# Fetch edges in Gremlin
2
g.E(<eid>)
3
 
           
4
# Fetch edges in Cypher
5
MATCH (n)-[r:label]->()
6
WHERE condition
7
RETURN properties(r)
8
 
           
9
# Fetch edges in nGQL
10
FETCH PROP ON <edge_name> <src_vid> -> <dst_vid>


Other Operations

In addition to the common CRUD on vertices and edges, we will also show you some combined queries in the three graph query languages.

Traverse Edges

Cypher
 




xxxxxxxxxx
1
10


 
1
# Traverse edges with specified vertices in Gremlin
2
g.V(<vid>).outE(<edge>)
3
 
           
4
# Traverse edges with specified vertices in Cypher
5
Match (n)->[r:label]->[]
6
WHERE id(n) = vid
7
RETURN r
8
 
           
9
# Traverse edges with specified vertices in nGQL
10
GO FROM <vid> OVER <edge>



Traverse Edges Reversely

In reverse traverse, Gremlin uses in to indicate reversion, Cypher uses <-. nGQL uses keyword REVERSELY.

Cypher
 




xxxxxxxxxx
1


 
1
# Traverse edges reversely with specified vertices Gremlin
2
g.V(<vid>).in(<edge>)
3
 
           
4
# Traverse edges reversely with specified vertices Cypher
5
MATCH (n)<-[r:label]-()
6
 
           
7
# Traverse edges reversely with specified vertices nGQL
8
GO FROM <vid>  OVER <edge> REVERSELY



Traverse Edges Bidirectionally

If the edge direction is irrelevant (either direction is acceptable), Gremlin uses bothE(), Cypher use -[]-, and nGQL use keyword BIDIRECT .

Cypher
 




xxxxxxxxxx
1


 
1
# Traverse edges reversely with specified vertices Gremlin
2
g.V(<vid>).bothE(<edge>)
3
 
           
4
# Traverse edges reversely with specified vertices Cypher
5
MATCH (n)-[r:label]-()
6
 
           
7
# Traverse edges reversely with specified vertices nGQL
8
GO FROM <vid>  OVER <edge> BIDIRECT



Query N Hops Along Specified Edge

Gremlin and nGQL use times and STEP respectively to represent N hops. Cypher uses relationship*N.

Cypher
 




xxxxxxxxxx
1
10


 
1
# Query N hops along specified edge in Gremlin
2
g.V(<vid>).repeat(out(<edge>)).times(N)
3
 
           
4
# Query N hops along specified edge in Cypher
5
MATCH (n)-[r:label*N]->()
6
WHERE condition
7
RETURN r
8
 
           
9
# Query N hops along specified edge in nGQL
10
GO N STEPS FROM <vid> OVER <edge>



Find Paths Between Two Vertices

Cypher
 




xxxxxxxxxx
1
10


 
1
# Find paths between two vertices in Gremlin
2
g.V(<vid>).repeat(out()).until(<vid>).path()
3
 
           
4
# Find paths between two vertices in Cypher
5
MATCH p =(a)-[.*]->(b)
6
WHERE condition
7
RETURN p
8
 
           
9
# Find paths between two vertices in nGQL
10
FIND ALL PATH FROM <vid> TO <vid> OVER *



Example Queries

This section introduces some demonstration queries.

Demo Model: The Graphs of Gods

The examples in this section make extensive use of the toy graph distributed with Janus Graph called The Graphs of Gods, as diagrammed below.

This example describes the relationships between the beings and places of the Roman pantheon.

Inserting Data

Cypher
 




xxxxxxxxxx
1
24


 
1
# Inserting vertices
2
## nGQL
3
nebula> INSERT VERTEX character(name, age, type) VALUES hash("saturn"):("saturn", 10000, "titan"), hash("jupiter"):("jupiter", 5000, "god");
4
## Gremlin
5
gremlin> saturn = g.addV("character").property(T.id, 1).property('name', 'saturn').property('age', 10000).property('type', 'titan').next();
6
==>v[1]
7
gremlin> jupiter = g.addV("character").property(T.id, 2).property('name', 'jupiter').property('age', 5000).property('type', 'god').next();
8
==>v[2]
9
gremlin> prometheus = g.addV("character").property(T.id, 31).property('name',  'prometheus').property('age', 1000).property('type', 'god').next();
10
==>v[31]
11
gremlin> jesus = g.addV("character").property(T.id, 32).property('name',  'jesus').property('age', 5000).property('type', 'god').next();
12
==>v[32]
13
## Cypher
14
cypher> CREATE (src:character {name:"saturn", age: 10000, type:"titan"})
15
cypher> CREATE (dst:character {name:"jupiter", age: 5000, type:"god"})
16
 
           
17
# Inserting edges
18
## nGQL
19
nebula> INSERT EDGE father() VALUES hash("jupiter")->hash("saturn"):();
20
## Gremlin
21
gremlin> g.addE("father").from(jupiter).to(saturn).property(T.id, 13);
22
==>e[13][2-father->1]
23
## Cypher
24
cypher> CREATE (src)-[rel:father]->(dst)



Deleting

Plain Text
 




xxxxxxxxxx
1


 
1
# nGQL
2
nebula> DELETE VERTEX hash("prometheus");
3
 
           
4
# Gremlin
5
gremlin> g.V(prometheus).drop();
6
 
           
7
# Cypher
8
cypher> MATCH (n:character {name:"prometheus"}) DETACH DELETE n 



Updating

Plain Text
 




xxxxxxxxxx
1


 
1
# nGQL
2
nebula> UPDATE VERTEX hash("jesus") SET character.type = 'titan';
3
 
           
4
# Gremlin
5
gremlin> g.V(jesus).property('age', 6000);
6
==>v[32]
7
 
           
8
# Cypher
9
cypher> MATCH (n:character {name:"jesus"}) SET n.type = 'titan';



Fetching/Reading

Plain Text
 




xxxxxxxxxx
1
19


 
1
# nGQL
2
nebula> FETCH PROP ON character hash("saturn");
3
===================================================
4
| character.name | character.age | character.type |
5
===================================================
6
| saturn         | 10000         | titan          |
7
---------------------------------------------------
8
 
           
9
# Gremlin
10
gremlin> g.V(saturn).valueMap();
11
==>[name:[saturn],type:[titan],age:[10000]]
12
 
           
13
# Cypher
14
cypher> MATCH (n:character {name:"saturn"}) RETURN properties(n)
15
  ╒════════════════════════════════════════════╕
16
  "properties(n)"                             
17
  ╞════════════════════════════════════════════╡
18
  {"name":"saturn","type":"titan","age":10000}
19
  └────────────────────────────────────────────┘



Finding the Name of Hercules's Father

Shell
 




xxxxxxxxxx
1
20


 
1
# nGQL
2
nebula>  LOOKUP ON character WHERE character.name == 'hercules' | \
3
      -> GO FROM $-.VertexID OVER father YIELD $$.character.name;
4
=====================
5
| $$.character.name |
6
=====================
7
| jupiter           |
8
---------------------
9
 
           
10
# Gremlin
11
gremlin> g.V().hasLabel('character').has('name','hercules').out('father').values('name');
12
==>jupiter
13
 
           
14
# Cypher
15
cypher> MATCH (src:character{name:"hercules"})-[:father]->(dst:character) RETURN dst.name
16
      ╒══════════╕
17
      │"dst.name"
18
      ╞══════════╡
19
      │"jupiter"
20
      └──────────┘



Finding the Name of Hercules's Grandfather

Shell
 




xxxxxxxxxx
1
20


 
1
# nGQL
2
nebula> LOOKUP ON character WHERE character.name == 'hercules' | \
3
     -> GO 2 STEPS FROM $-.VertexID OVER father YIELD $$.character.name;
4
=====================
5
| $$.character.name |
6
=====================
7
| saturn            |
8
---------------------
9
 
           
10
# Gremlin
11
gremlin> g.V().hasLabel('character').has('name','hercules').out('father').out('father').values('name');
12
==>saturn
13
 
           
14
# Cypher
15
cypher> MATCH (src:character{name:"hercules"})-[:father*2]->(dst:character) RETURN dst.name
16
      ╒══════════╕
17
      │"dst.name"
18
      ╞══════════╡
19
      │"saturn"
20
      └──────────┘



Find the Characters With Age > 100

Shell
 




xxxxxxxxxx
1
34


 
1
# nGQL
2
nebula> LOOKUP ON character WHERE character.age > 100 YIELD character.name, character.age;
3
=========================================================
4
| VertexID             | character.name | character.age |
5
=========================================================
6
| 6761447489613431910  | pluto          | 4000          |
7
---------------------------------------------------------
8
| -5860788569139907963 | neptune        | 4500          |
9
---------------------------------------------------------
10
| 4863977009196259577  | jupiter        | 5000          |
11
---------------------------------------------------------
12
| -4316810810681305233 | saturn         | 10000         |
13
---------------------------------------------------------
14
 
           
15
# Gremlin
16
gremlin> g.V().hasLabel('character').has('age',gt(100)).values('name');
17
==>saturn
18
==>jupiter
19
==>neptune
20
==>pluto
21
 
           
22
# Cypher
23
cypher> MATCH (src:character) WHERE src.age > 100 RETURN src.name
24
      ╒═══════════╕
25
      │"src.name"
26
      ╞═══════════╡
27
      │  "saturn"
28
      ├───────────┤
29
      │ "jupiter"
30
      ├───────────┤
31
      │ "neptune"
32
      │───────────│
33
      │  "pluto"
34
      └───────────┘



Find Who Are Pluto's Cohabitants, Excluding Pluto Himself

Shell
 




xxxxxxxxxx
1
20


 
1
# nGQL
2
nebula>  GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \
3
$$.character.name != "pluto" YIELD $$.character.name AS cohabitants;
4
===============
5
| cohabitants |
6
===============
7
| cerberus    |
8
---------------
9
 
           
10
# Gremlin
11
gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name');
12
==>cerberus
13
 
           
14
# Cypher
15
cypher> MATCH (src:character{name:"pluto"})-[:lives]->()<-[:lives]-(dst:character) RETURN dst.name
16
      ╒══════════╕
17
      │"dst.name"
18
      ╞══════════╡
19
      │"cerberus"
20
      └──────────┘



Pluto's Brothers

Shell
 




xxxxxxxxxx
1
28


 
1
# which brother lives in which place?
2
3
## nGQL
4
nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS god | \
5
GO FROM $-.god OVER lives YIELD $^.character.name AS Brother, $$.location.name AS Habitations;
6
=========================
7
| Brother | Habitations |
8
=========================
9
| jupiter | sky         |
10
-------------------------
11
| neptune | sea         |
12
-------------------------
13
14
## Gremlin
15
gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name');
16
==>[god:jupiter, place:sky]
17
==>[god:neptune, place:sea]
18
19
## Cypher
20
cypher> MATCH (src:Character{name:"pluto"})-[:brother]->(bro:Character)-[:lives]->(dst)
21
RETURN bro.name, dst.name
22
      ╒═════════════════════════╕
23
      │"bro.name"    │"dst.name"
24
      ╞═════════════════════════╡
25
      │ "jupiter"    │  "sky"   │
26
      ├─────────────────────────┤
27
      │ "neptune"    │ "sea"    │
28
      └─────────────────────────┘



In addition to the basic operations in the three graph query languages, we will work on another piece of comparison of advanced operations in these languages. Stay tuned!

Topics:
graph database, graph database management, graph databases, graph query, graph query languages

Published at DZone with permission of Jamie Liu . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}