Comparison Between Gremlin and nGQL¶
Introduction to Gremlin¶
Gremlin is a graph traversal language developed by Apache TinkerPop. 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.
Introduction to nGQL¶
Nebula Graph introduces its own query language, nGQL, which is a declarative, textual query language like SQL, but for graphs. Unlike SQL, nGQL
is all about expressing graph patterns. The features of nGQL
are as follows:
- Syntax is close to SQL, but not exactly the same (Easy to learn)
- Expandable
- Keyword is case insensitive
- Support basic graph traverse
- Support pattern matching
- Support aggregation
- Support graph mutation
- Support distributed transaction (future release)
- Statement composition, but NO statement embedding (Easy to read)
Conceptual Comparisons¶
Name | Gremlin | nGQL |
---|---|---|
vertex, node | vertex | vertex |
edge, relationship | edge | edge |
vertex type | label | tag |
edge type | label | edge type |
vertex id | vid | vid |
edge id | eid | not support |
In Gremlin and nGQL, vertices and edges are identified with unique identifiers. In Nebula Graph, you can either specify identifiers or generate automatically with the hash or uuid function.
Basic Graph Operations¶
Name | Gremlin | nGQL |
---|---|---|
Create a new graph | g = TinkerGraph.open().traversal() | CREATE SPACE gods |
Show vertices' types | g.V().label() | SHOW TAGS |
Insert a vertex with a specified type | g.addV(String vertexLabel).property() | INSERT VERTEX <tag_name> (prop_name_list) VALUES <vid> :(prop_value_list) |
Insert an edge with specified edge type | g.addE(String edgeLabel).from(v1).to(v2).property() | INSERT EDGE <edge_name> ( <prop_name_list> ) VALUES <src_vid> -> <dst_vid> : ( <prop_value_list> ) |
Delete a vertex | g.V(<vid> ).drop() |
DELETE VERTEX <vid> |
Delete an edge | g.E(<vid> ).outE(<type> ).where(otherV().is(<vid> ))drop() |
DELETE EDGE <edge_type> <src_vid> -> <dst_vid> |
Update a vertex property | g.V(<vid> ).property() |
UPDATE VERTEX <vid> SET <update_columns> |
Fetch vertices with ID | g.V(<vid> ) |
FETCH PROP ON <tag_name> <vid> |
Fetch edges with ID | g.E(<src_vid> >> <dst_vid> ) |
FETCH PROP ON <edge_name> <src_vid> -> <dst_vid> |
Query a vertex along specified edge type | g.V(<vid> ).outE( <edge> ) |
GO FROM <vid> OVER <edge> |
Query a vertex along specified edge type reversely | g.V(<vid> ).in( <edge> ) |
GO FROM <vid> OVER <edge> REVERSELY |
Query N hops along a specified edge | g.V(<vid> ).repeat(out(<edge> )).times(N) |
GO N STEPS FROM <vid> OVER <edge> |
Find path between two vertices | g.V(<vid> ).repeat(out()).until(<vid> ).path() |
FIND ALL PATH FROM <vid> TO <vid> OVER * |
Example Queries¶
The examples in this section make extensive use of the toy graph distributed with Janus Graph called The Graphs of Gods. This graph is diagrammed below. The abstract data model is known as a Property Graph Model and this particular instance describes the relationships between the beings and places of the Roman pantheon.
- Insert data
# insert vertex
nebula> INSERT VERTEX character(name,age, type) VALUES hash("saturn"):("saturn", 10000, "titan"), hash("jupiter"):("jupiter", 5000, "god");
gremlin> saturn = g.addV("character").property(T.id, 1).property('name', 'saturn').property('age', 10000).property('type', 'titan').next();
==>v[1]
gremlin> jupiter = g.addV("character").property(T.id, 2).property('name', 'jupiter').property('age', 5000).property('type', 'god').next();
==>v[2]
gremlin> prometheus = g.addV("character").property(T.id, 31).property('name', 'prometheus').property('age', 1000).property('type', 'god').next();
==>v[31]
gremlin> jesus = g.addV("character")property(T.id, 32).property('name', 'jesus').property('age', 5000).property('type', 'god').next();
==>v[32]
# insert edge
nebula> INSERT EDGE father() VALUES hash("jupiter")->hash("saturn"):();
gremlin> g.addE("father").from(jupiter).to(saturn).property(T.id, 13);
==>e[13][2-father->1]
- Delete vertex
nebula> DELETE VERTEX hash("prometheus");
gremlin> g.V(prometheus).drop();
- Update vertex
nebula> UPDATE VERTEX hash("jesus") SET character.type = 'titan';
gremlin> g.V(jesus).property('age', 6000);
- Fetch data
nebula> FETCH PROP ON character hash("saturn");
==================================================
| character.name | character.age |character.type |
==================================================
| saturn | 10000 |titan |
--------------------------------------------------
gremlin> g.V(saturn).valueMap();
==>[name:[saturn],type:[titan],age:[10000]]
- Find the name of hercules's grandfather
nebula> LOOKUP ON character WHERE character.name == 'hercules' | \
-> GO 2 STEPS FROM $-.VertexID OVER father YIELD $$.character.name;
=====================
| $$.character.name |
=====================
| saturn |
---------------------
gremlin> g.V().hasLabel('character').has('name','hercules').out('father').out('father').values('name');
==>saturn
- Find the name of hercules's father
nebula> LOOKUP ON character WHERE character.name == 'hercules' | \
-> GO FROM $-.VertexID OVER father YIELD $$.character.name;
=====================
| $$.character.name |
=====================
| jupiter |
---------------------
gremlin> g.V().hasLabel('character').has('name','hercules').out('father').values('name');
==>jupiter
- Find the characters with age > 100
nebula> LOOKUP ON character WHERE character.age > 100 YIELD character.name;
=========================================
| VertexID | character.name |
=========================================
| 6761447489613431910 | pluto |
-----------------------------------------
| -5860788569139907963 | neptune |
-----------------------------------------
| 4863977009196259577 | jupiter |
-----------------------------------------
| -4316810810681305233 | saturn |
-----------------------------------------
gremlin> g.V().hasLabel('character').has('age',gt(100)).values('name');
==>saturn
==>jupiter
==>neptune
==>pluto
- Find who are pluto's cohabitants
nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | \
GO FROM $-.place OVER lives REVERSELY YIELD $$.character.name AS cohabitants;
===============
| cohabitants |
===============
| pluto |
---------------
| cerberus |
---------------
gremlin> g.V(pluto).out('lives').in('lives').values('name');
==>pluto
==>cerberus
- pluto can't be his own cohabitant
nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \
$$.character.name != "pluto" YIELD $$.character.name AS cohabitants;
===============
| cohabitants |
===============
| cerberus |
---------------
gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name');
==>cerberus
- Pluto's Brothers
# where do pluto's brothers live?
nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS brother | \
GO FROM $-.brother OVER lives YIELD $$.location.name;
====================
| $$.location.name |
====================
| sky |
--------------------
| sea |
--------------------
gremlin> g.V(pluto).out('brother').out('lives').values('name');
==>sky
==>sea
# which brother lives in which place?
nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS god | \
GO FROM $-.god OVER lives YIELD $^.character.name AS Brother, $$.location.name AS Habitations;
=========================
| Brother | Habitations |
=========================
| jupiter | sky |
-------------------------
| neptune | sea |
-------------------------
gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name');
==>[god:jupiter, place:sky]
==>[god:neptune, place:sea]
Advance Queries¶
Graph Exploration¶
# Gremlin version
gremlin> Gremlin.version();
==>3.3.5
# Return all the vertices
gremlin> g.V();
==>v[1]
==>v[2]
...
nebula> # Coming soon
# Count all the vertices
gremlin> g.V().count();
==>12
nebula> # Coming soon
# Count the vertices and edges by label
gremlin> g.V().groupCount().by(label);
==>[character:9,location:3]
gremlin> g.E().groupCount().by(label);
==>[mother:1,lives:5,father:2,brother:6,battled:3,pet:1]
nebula> # Coming soon
# Return all edges
gremlin> g.E();
==>e[13][2-father->1]
==>e[14][2-lives->3]
...
nebula> # Coming soon
# Return vertices labels
gremlin> g.V().label().dedup();
==>character
==>location
nebula> SHOW TAGS;
==================
| ID | Name |
==================
| 15 | character |
------------------
| 16 | location |
------------------
# Return edge types
gremlin> g.E().label().dedup();
==>father
==>lives
...nebula> SHOW EDGES;
================
| ID | Name |
================
| 17 | father |
----------------
| 18 | brother |
----------------
...
# Return all vertices properties
gremlin> g.V().valueMap();
==>[name:[saturn],type:[titan],age:[10000]]
==>[name:[jupiter],type:[god],age:[5000]]
...
nebula> # Coming soon
# Return properties of vertices labeled character
gremlin> g.V().hasLabel('character').valueMap();
==>[name:[saturn],type:[titan],age:[10000]]
==>[name:[jupiter],type:[god],age:[5000]]
...
Traversing Edges¶
Name | Gremlin | nGQL |
---|---|---|
Out adjacent vertices to the vertex | out(\ | GO FROM \ |
In adjacent vertices to the vertex | in(\ | GO FROM \ |
Both adjacent vertices of the vertex | both(\ | GO FROM \ |
# Find the out adjacent vertices of a vertex along an edge
gremlin> g.V(jupiter).out('brother');
==>v[8]
==>v[5]
nebula> GO FROM hash("jupiter") OVER brother;
========================
| brother._dst |
========================
| 6761447489613431910 |
------------------------
| -5860788569139907963 |
------------------------
# Find the in adjacent vertices of a vertex along an edge
gremlin> g.V(jupiter).in('brother');
==>v[5]
==>v[8]
nebula> GO FROM hash("jupiter") OVER brother REVERSELY;
=======================
| brother._dst |
=======================
| 4863977009196259577 |
-----------------------
| 4863977009196259577 |
-----------------------
# Find the both adjacent vertices of a vertex along an edge
gremlin> g.V(jupiter).both('brother');
==>v[8]
==>v[5]
==>v[5]
==>v[8]
nebula> GO FROM hash("jupiter") OVER brother BIDIRECT;
=======================
| brother._dst |
=======================
| 6761447489613431910 |
------------------------
| -5860788569139907963|
| 4863977009196259577 |
-----------------------
| 4863977009196259577 |
-----------------------
# Two hops out traverse
gremlin> g.V(hercules).out('father').out('lives');
==>v[3]
nebula> GO FROM hash("hercules") OVER father YIELD father._dst AS id | \
GO FROM $-.id OVER lives;
========================
| lives._dst |
========================
| -1121386748834253737 |
------------------------
Has Filter Condition¶
Name | Gremlin | nGQL |
---|---|---|
Filter vertex via identifier | hasId(\ |
FETCH PROP ON \ |
Filter vertex or edge via label, key and value | has(\ | LOOKUP \ |
# Filter vertex with ID saturn
gremlin> g.V().hasId(saturn);
==>v[1]
nebula> FETCH PROP ON * hash("saturn");
==========================================================================
| VertexID | character.name | character.age | character.type |
==========================================================================
| -4316810810681305233 | saturn | 10000 | titan |
--------------------------------------------------------------------------
# Find for vertices with tag "character" and "name" attribute value "hercules"
gremlin> g.V().has('character','name','hercules').valueMap();
==>[name:[hercules],type:[demigod],age:[30]]
nebula> LOOKUP ON character WHERE character.name == 'hercules' YIELD character.name, character.age, character.type;
=========================================================================
| VertexID | character.name | character.age | character.type |
=========================================================================
| 5976696804486077889 | hercules | 30 | demigod |
-------------------------------------------------------------------------
Limiting Returned Results¶
Name | Gremlin | nGQL |
---|---|---|
Constrain the number of rows to return | limit() | LIMIT |
Emit the last n-objects | tail() | ORDER BY \ |
Skip n-objects | skip() | LIMIT \ |
# Find the first two records
gremlin> g.V().has('character','name','hercules').out('battled').limit(2);
==>v[9]
==>v[10]
nebula> GO FROM hash('hercules') OVER battled | LIMIT 2;
=======================
| battled._dst |
=======================
| 530133512982221454 |
-----------------------
| -695163537569412701 |
-----------------------
# Find the last record
gremlin> g.V().has('character','name','hercules').out('battled').values('name').tail(1);
==>cerberus
nebula> GO FROM hash('hercules') OVER battled YIELD $$.character.name AS name | ORDER BY name | LIMIT 1;
============
| name |
============
| cerberus |
------------
# Skip the first record and return one record
gremlin> g.V().has('character','name','hercules').out('battled').values('name').skip(1).limit(1);
==>hydra
nebula> GO FROM hash('hercules') OVER battled YIELD $$.character.name AS name | ORDER BY name | LIMIT 1,1;
=========
| name |
=========
| hydra |
---------
Finding Path¶
Name | Gremlin | nGQL |
---|---|---|
All path | path() | FIND ALL PATH |
Exclude cycles path | simplePath() | \ |
Only cycles path | cyclicPath() | \ |
Shortest path | \ | FIND SHORTEST PATH |
NOTE: Nebula Graph requires the source vertex and the destination vertex to find path while Gremlin only needs the source vertex.
# Find path from vertex pluto to the out adjacent vertices
gremlin> g.V().hasLabel('character').has('name','pluto').out().path();
==>[v[8],v[12]]
==>[v[8],v[2]]
==>[v[8],v[5]]
==>[v[8],v[11]]
# Find the shortest path from vertex pluto to vertex jupiter
nebula> LOOKUP ON character WHERE character.name== "pluto" YIELD character.name AS name | \
FIND SHORTEST PATH FROM $-.VertexID TO hash("jupiter") OVER *;
============================================================
| _path_ |
============================================================
| 6761447489613431910 <brother,0> 4863977009196259577
------------------------------------------------------------
Traversing N Hops¶
Name | Gremlin | nGQL |
---|---|---|
Loop over a traversal | repeat() | N STEPS |
Times the traverser has gone through a loop | times() | N STEPS |
Specify when to end the loop | until() | \ |
Specify when to collect data | emit() | \ |
# Find vertex pluto's out adjacent neighbors
gremlin> g.V().hasLabel('character').has('name','pluto').repeat(out()).times(1);
==>v[12]
==>v[2]
==>v[5]
==>v[11]
nebula> LOOKUP ON character WHERE character.name== "pluto" YIELD character.name AS name | \
GO FROM $-.VertexID OVER *;
================================================================================================================
| father._dst | brother._dst | lives._dst | mother._dst | pet._dst | battled._dst |
================================================================================================================
| 0 | -5860788569139907963 | 0 | 0 | 0 | 0 |
----------------------------------------------------------------------------------------------------------------
| 0 | 4863977009196259577 | 0 | 0 | 0 | 0 |
----------------------------------------------------------------------------------------------------------------
| 0 | 0 | -4331657707562925133 | 0 | 0 | 0 |
----------------------------------------------------------------------------------------------------------------
| 0 | 0 | 0 | 0 | 4594048193862126013 | 0 |
----------------------------------------------------------------------------------------------------------------
# Find path between vertex hercules and vertex cerberus
# Stop traversing when the destination vertex is cerberus
gremlin> g.V().hasLabel('character').has('name','hercules').repeat(out()).until(has('name', 'cerberus')).path();
==>[v[6],v[11]]
==>[v[6],v[2],v[8],v[11]]
==>[v[6],v[2],v[5],v[8],v[11]]
...
nebula> # Coming soon
# Find path sourcing from vertex hercules
# And the destination vertex type is character
gremlin> g.V().hasLabel('character').has('name','hercules').repeat(out()).emit(hasLabel('character')).path();
==>[v[6],v[7]]
==>[v[6],v[2]]
==>[v[6],v[9]]
==>[v[6],v[10]]
...
nebula> # Coming soon
# Find shortest path between pluto and saturn over any edge
# And the deepest loop is 3
gremlin> g.V('pluto').repeat(out().simplePath()).until(hasId('saturn').and().loops().is(lte(3))).hasId('saturn').path();
nebula> FIND SHORTEST PATH FROM hash('pluto') TO hash('saturn') OVER * UPTO 3 STEPS;
=================================================================================================
| _path_ |
=================================================================================================
| 6761447489613431910 <brother,0> 4863977009196259577 <father,0> -4316810810681305233
-------------------------------------------------------------------------------------------------
Ordering Results¶
Name | Gremlin | nGQL |
---|---|---|
Order the items increasingly | order().by() | ORDER BY |
Order the items decreasingly | order().by(decr) | ORDER BY DESC |
Randomize the records order | order().by(shuffle) | \ |
# Find pluto's brother and order by age decreasingly.
gremlin> g.V(pluto).out('brother').order().by('age', decr).valueMap();
==>[name:[jupiter],type:[god],age:[5000]]
==>[name:[neptune],type:[god],age:[4500]]
nebula> GO FROM hash('pluto') OVER brother YIELD $$.character.name AS Name, $$.character.age as Age | ORDER BY Age DESC;
==================
| Name | Age |
==================
| jupiter | 5000 |
------------------
| neptune | 4500 |
------------------
Group By¶
Name | Gremlin | nGQL |
---|---|---|
Group by items | group().by() | GROUP BY |
Remove repeated items | dedup() | DISTINCT |
Group by items and count | groupCount() | GROUP BY COUNT |
NOTE: The GROUP BY function can only be applied in the YIELD clause.
# Group vertices by label then count
gremlin> g.V().group().by(label).by(count());
==>[character:9,location:3]
nebula> # Coming soon
# Find vertex jupiter's out adjacency vertices, group by name, then count
gremlin> g.V(jupiter).out().group().by('name').by(count());
==>[sky:1,saturn:1,neptune:1,pluto:1]
nebula> GO FROM hash('jupiter') OVER * YIELD $$.character.name AS Name, $$.character.age as Age, $$.location.name | \
GROUP BY $-.Name YIELD $-.Name, COUNT(*);
======================
| $-.Name | COUNT(*) |
======================
| | 1 |
----------------------
| pluto | 1 |
----------------------
| saturn | 1 |
----------------------
| neptune | 1 |
----------------------
# Find the distinct destination vertices sourcing from vertex jupiter
gremlin> g.V(jupiter).out().hasLabel('character').dedup();
==>v[1]
==>v[8]
==>v[5]
nebula> GO FROM hash('jupiter') OVER * YIELD DISTINCT $$.character.name, $$.character.age, $$.location.name;
===========================================================
| $$.character.name | $$.character.age | $$.location.name |
===========================================================
| pluto | 4000 | |
-----------------------------------------------------------
| neptune | 4500 | |
-----------------------------------------------------------
| saturn | 10000 | |
-----------------------------------------------------------
| | 0 | sky |
-----------------------------------------------------------
Where Filter Condition¶
Name | Gremlin | nGQL |
---|---|---|
Where filter condition | where() | WHERE |
Predicates comparison:
Name | Gremlin | nGQL |
---|---|---|
Equal to | eq(object) | == |
Not equal to | neq(object) | != |
Less than | lt(number) | < |
Less than or equal to | lte(number) | <= |
Greater than | gt(number) | > |
Greater than or equal to | gte(number) | >= |
Whether a value is within the array | within(objects…) | udf_is_in() |
gremlin> eq(2).test(3);
==>false
nebula> YIELD 3 == 2;
==========
| (3==2) |
==========
| false |
----------
gremlin> within('a','b','c').test('d');
==>false
nebula> YIELD udf_is_in('d', 'a', 'b', 'c');
======================
| udf_is_in(d,a,b,c) |
======================
| false |
----------------------
# Find pluto's co-habitants and exclude himself
gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name');
==>cerberus
nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \
$$.character.name != "pluto" YIELD $$.character.name AS cohabitants;
===============
| cohabitants |
===============
| cerberus |
---------------
Logical Operators¶
Name | Gremlin | nGQL |
---|---|---|
Is | is() | == |
Not | not() | != |
And | and() | AND |
Or | or() | OR |
# Find age greater than or equal to 30
gremlin> g.V().values('age').is(gte(30));
==>10000
==>5000
==>4500
==>30
==>45
==>4000
nebula> LOOKUP ON character WHERE character.age >= 30 YIELD character.age;
========================================
| VertexID | character.age |
========================================
| -4316810810681305233 | 10000 |
---------------------------------------–
| 4863977009196259577 | 5000 |
---------------------------------------–
| -5860788569139907963 | 4500 |
---------------------------------------–
| 5976696804486077889 | 30 |
---------------------------------------–
| -6780323075177699500 | 45 |
---------------------------------------–
| 6761447489613431910 | 4000 |
---------------------------------------–
# Find character with name pluto and age 4000
gremlin> g.V().has('name','pluto').and().has('age',4000);
==>v[8]
nebula> LOOKUP ON character WHERE character.name == 'pluto' AND character.age == 4000;
=======================
| VertexID |
=======================
| 6761447489613431910 |
-----------------------
# Logical not
gremlin> g.V().has('name','pluto').out('brother').not(values('name').is('neptune')).values('name');
==>jupiter
nebula> LOOKUP ON character WHERE character.name == 'pluto' YIELD character.name AS name | \
GO FROM $-.VertexID OVER brother WHERE $$.character.name != 'neptune' YIELD $$.character.name;
=====================
| $$.character.name |
=====================
| jupiter |
---------------------
Statistical Operations¶
Name | Gremlin | nGQL |
---|---|---|
Sum | sum() | SUM() |
Max | max() | MAX() |
Min | min() | MIN() |
Mean | mean() | AVG() |
Nebula Graph statistical operations must be applied with
GROUP BY
.
# Calculate the sum of ages of all characters
gremlin> g.V().hasLabel('character').values('age').sum();
==>23595
nebula> # Coming soon
# Calculate the sum of the out brother edges of all characters
gremlin> g.V().hasLabel('character').map(outE('brother').count()).sum();
==>6
nebula> # Coming soon
# Return the max age of all characters
gremlin> g.V().hasLabel('character').values('age').max();
==>10000
nebula> # Coming soon
Selecting and Filtering Paths¶
# Select the results of steps 1 and 3 from the path as the final result
gremlin> g.V(pluto).as('a').out().as('b').out().as('c').select('a','c');
==>[a:v[8],c:v[3]]
==>[a:v[8],c:v[1]]
...
nebula> # Coming soon
# Specify dimensions via by()
gremlin> g.V(pluto).as('a').out().as('b').out().as('c').select('a','c').by('name');
==>[a:pluto,c:sky]
==>[a:pluto,c:saturn]
...
nebula> # Coming soon
# Selects the specified key value from the map
gremlin> g.V().valueMap().select('name').dedup();
==>[saturn]
==>[jupiter]
...
nebula> # Coming soon
Branches¶
# Traverse all vertices with label 'character'
# If name is 'jupiter', return the age property
# Else return the name property
gremlin> g.V().hasLabel('character').choose(values('name')).option('jupiter', values('age')).option(none, values('name'));
==>saturn
==>5000
==>neptune
...
# Lambda
gremlin> g.V().branch {it.get().value('name')}.option('jupiter', values('age')).option(none, values('name'));
==>saturn
==>5000
...
# Traversal
gremlin> g.V().branch(values('name')).option('jupiter', values('age')).option(none, values('name'));
==>saturn
==>5000
# Branch
gremlin> g.V().choose(has('name','jupiter'),values('age'),values('name'));
==>saturn
==>5000
# Group based on if then
gremlin> g.V().hasLabel("character").groupCount().by(values("age").choose(
is(lt(40)),constant("young"),
choose(is(lt(4500)),
constant("old"),
constant("very old"))));
==>[young:4,old:2,very old:3]
Similar function is yet to be supported in Nebula Graph.
Coalesce¶
The coalesce()
step evaluates the provided traversals in order and returns the first traversal that emits at least one element.
The optional()
step returns the result of the specified traversal if it yields a result else it returns the calling element, i.e. the identity().
The union()
step supports the merging of the results of an arbitrary number of traversals.
# If type is monster, return type. Else return 'Not a monster'.
gremlin> g.V(pluto).coalesce(has('type','monster').values('type'),constant("Not a monster"));
==>Not a monster
# Find the following edges and adjacent vertices of jupiter in order, and stop when finding one
# 1. Edge brother out adjacent vertices
# 2. Edge father out adjacent vertices
# 3. Edge father in adjacent vertices
gremlin> g.V(jupiter).coalesce(outE('brother'), outE('father'), inE('father')).inV().path().by('name').by(label);
==>[jupiter,brother,pluto]
==>[jupiter,brother,neptune]
# Find pluto's father, if there is not any then return pluto himself
gremlin> g.V(pluto).optional(out('father')).valueMap();
==>[name:[pluto],type:[god],age:[4000]]
# Find pluto's father and brother, union the results then return the paths
gremlin> g.V(pluto).union(out('father'),both('brother')).path();
==>[v[8],v[2]]
==>[v[8],v[5]]
Similar function is yet to be supported in Nebula Graph.
Aggregating and Unfolding Results¶
# Collect results of the first step into set x
# Note: This operation doesn't affect subsequent results
gremlin> g.V(pluto).out().aggregate('x');
==>v[12]
==>v[2]
...
# Specify the aggregation dimensions via by ()
gremlin> g.V(pluto).out().aggregate('x').by('name').cap('x');
==>[tartarus,jupiter,neptune,cerberus]
# Find pluto's 2 hop out adjacent neighbors
# Collect the results in set x
# Show the neighbors' name
gremlin> g.V(pluto).out().aggregate('x').out().aggregate('x').cap('x').unfold().values('name');
==>tartarus
==>tartarus
...
Similar function is yet to be supported in Nebula Graph.
Matching Patterns¶
The match()
step provides a more declarative form of graph querying based on the notion of pattern matching. With match(), the user provides a collection of "traversal fragments," called patterns, that have variables defined that must hold true throughout the duration of the match().
# Matching each vertex with the following pattern. If pattern is met, return map<String, Object>, els filter it.
# Pattern 1: a is jupiter's son
# Pattern 2: b is jupiter
# Pattern 3: c is jupiter's brother, whose age is 4000
gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c'));
==>[a:v[6],b:v[2],c:v[8]]
# match() can be applied with select() to select partial results from Map <String, Object>
gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c')).select('a', 'c').by('name');
==>[a:hercules,c:pluto]
# match () can be applied with where () to filter the results
gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c')).where('a', neq('c')).select('a', 'c').by('name');
==>[a:hercules,c:pluto]
Random filtering¶
The sample()
step accepts an integer value and samples the maximum number of the specified results randomly from the previous traverser.
The coin()
step can randomly filter out a traverser with the given probability. You give coin a value indicating how biased the toss should be.
# Randomly select 2 out edges from all vertices
gremlin> g.V().outE().sample(2);
==>e[15][2-brother->5]
==>e[18][5-brother->2]
# Pick 3 names randomly from all vertices
gremlin> g.V().values('name').sample(3);
==>hercules
==>sea
==>jupiter
# Pick 3 randomly from all characters based on age
gremlin> g.V().hasLabel('character').sample(3).by('age');
==>v[1]
==>v[2]
==>v[6]
# Applied with local to do random walk
# Starting from pluto, conduct random walk 3 times
gremlin> g.V(pluto).repeat(local(bothE().sample(1).otherV())).times(3).path();
==>[v[8],e[26][8-brother->5],v[5],e[18][5-brother->2],v[2],e[13][2-father->1],v[1]]
# Filter each vertex with a probability of 0.5
gremlin> g.V().coin(0.5);
==>v[1]
==>v[2]
...
# Return the name attribute of all vertices labeled location, otherwise return not a location
gremlin> g.V().choose(hasLabel('location'), values('name'), constant('not a location'));
==>not a location
==>not a location
==>sky
...
Sack¶
A traverser that contains a local data structure is called a "sack". The sack()
step is used to read and write sacks. Each sack of each traverser is created with withSack()
.
# Defines a Gremlin sack with a value of one and return values in the sack
gremlin> g.withSack(1).V().sack();
==>1
==>1
...
Barrier¶
The barrier()
step turns the lazy traversal pipeline into a bulk-synchronous pipeline. It's useful when everything prior to barrier() needs to be executed before moving onto the steps after the barrier().
# Calculate the Eigenvector Centrality with barrier
# Including groupCount and cap, sorted in descending order
gremlin> g.V().repeat(both().groupCount('m')).times(5).cap('m').order(local).by(values, decr);
Local¶
A GraphTraversal operates on a continuous stream of objects. In many situations, it is important to operate on a single element within that stream. To do such object-local traversal computations, local()
step exists.
# Without local()
gremlin> g.V().hasLabel('character').as('character').properties('age').order().by(value,decr).limit(2).value().as('age').select('character', 'age').by('name').by();
==>[character:saturn,age:10000]
==>[character:jupiter,age:5000]
# With local()
gremlin> g.V().hasLabel('character').as('character').local(properties('age').order().by(value).limit(2)).value().as('age').select('character', 'age').by('name').by()
==>[character:saturn,age:10000]
==>[character:jupiter,age:5000]
==>[character:neptune,age:4500]
==>[character:hercules,age:30]
...
# Return the property map of monster
gremlin> g.V()hasLabel('character').has('type', 'type').propertyMap();
==>[name:[vp[name->nemean]],type:[vp[type->monster]],age:[vp[age->20]]]
==>[name:[vp[name->hydra]],type:[vp[type->monster]],age:[vp[age->0]]]
==>[name:[vp[name->cerberus]],type:[vp[type->monster]],age:[vp[age->0]]]
# Find number of monster
gremlin> g.V()hasLabel('character').has('type', 'monster').propertyMap().count(local);
==>3
==>3
==>3
# Find the max vertices number labeled tha same tag
gremlin> g.V().groupCount().by(label).select(values).max(local);
==>9
# List the first attribute of all vertices
gremlin> g.V().valueMap().limit(local, 1);
==>[name:[saturn]]
==>[name:[jupiter]]
==>[name:[sky]]
...
# Without local
gremlin> g.V().valueMap().limit(1);
==>[name:[saturn],type:[titan],age:[10000]]
# All vertices as a set, sample 2 from it
gremlin> g.V().fold().sample(local,2);
==>[v[8],v[1]]
Statistics and Analysis¶
Gremlin provides two steps for statistics and analysis of the executed query statements:
- The
explain()
step will return a TraversalExplanation. A traversal explanation details how the traversal (prior to explain()) will be compiled given the registered traversal strategies. - The
profile()
step allows developers to profile their traversals to determine statistical information like step runtime, counts, etc.