Querying using Gremlin
PuppyGraph supports Gremlin, the graph query language developed by Apache TinkerPop.
Interactive Query UI
The Gremlin query tool in Graph Query offers a notebook-like user experience, complemented by the ability to visualize query results.
To access it, click the Graph Query tab on the Query page, then select the Gremlin query type.
Gremlin Console
PuppyGraph provides an interactive CLI for Gremlin queries.
To access it, click the Gremlin Console tab on the Query page.
Graph Notebook
Graph Notebook is an open-source tool that enables users to interact with and visualize graph databases directly within a Jupyter Notebook environment.
To use Graph Notebook with PuppyGraph, you need to set up a separate JupyterLab environment. Follow these steps:
Prerequisites
- Docker installed and running
uvpackage manager (install from astral.sh)
Setup Graph Notebook Environment
- Create a Python environment and install required packages:
uv python install 3.11
uv venv --python 3.11 --seed
uv pip install "jupyterlab>=4.3.5,<5" graph-notebook
- Launch JupyterLab:
-
Open the URL displayed in the terminal output (typically
http://localhost:8888/?token=...) -
Create a new Python 3 notebook and initialize Graph Notebook (first run only):
Connecting to PuppyGraph
Configure the connection to use Gremlin queries:
%%graph_notebook_config
{
"host": "localhost",
"port": 8182,
"ssl": false,
"gremlin": {
"traversal_source": "g",
"username": "your_username",
"password": "your_password",
"message_serializer": "graphbinaryv1"
}
}
Note: Replace your_username and your_password with the same credentials you used when starting PuppyGraph.
Example Gremlin Queries:
Count vertices:
Count edges:
Explore graph structure:
Client Drivers
PuppyGraph supports connecting and executing queries through Tinkerpop Gremlin client drivers.
See also Gremlin Drivers and Variants for more details on the drivers.
Python
Install the Gremlin Python Driver:
The following is an example of connecting to PuppyGraph and executing a query.
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
# Establish a Gremlin Server connection to PuppyGraph.
g = traversal().with_remote(
DriverRemoteConnection('ws://localhost:8182/gremlin', 'g',
username='puppygraph', password='puppygraph123'))
# Get all nodes (vertices) in the graph.
nodes = g.V().value_map().to_list()
print("All nodes (vertices) in the graph:")
print(nodes)
It is also possible to set a timeout for the query execution.
In this example, the whole query is submitted as a string, which is useful for graph algorithms.
from gremlin_python.driver import client
query = """
graph.program(
PageRankProgram.build()
.maxIteration(10)
.vertices("users")
.edges("friends")
.setParams("tolerance", 0.0d)
.create()
).submitAndGet()
.toList()
"""
cli = client.Client(f'ws://localhost:8182/gremlin', 'g',
username='puppygraph',
password='puppygraph123')
query_result = cli.submit(query)
result = query_result.all().result()
for record in result:
print(record)
cli.close()
Go
Install the Gremlin Go Driver:
The following is an example of connecting to PuppyGraph with the driver and executing a query.
package main
import (
"fmt"
"github.com/apache/tinkerpop/gremlin-go/v3/driver"
)
func main() {
// Establish a connection to the PuppyGraph database.
driverRemoteConnection, err := gremlingo.NewDriverRemoteConnection("ws://localhost:8182/gremlin")
if err != nil {
fmt.Println("Error creating connection:", err)
return
}
defer driverRemoteConnection.Close() // Ensure closure of connection after execution
// Create a graph traversal source.
g := gremlingo.Traversal_().WithRemote(driverRemoteConnection)
// Get all nodes (vertices) from the graph.
fmt.Println("All nodes (vertices) in the graph:")
results, err := g.V().ValueMap().ToList()
if err != nil {
fmt.Println("Error retrieving nodes (vertices):", err)
return
}
for _, r := range results {
fmt.Println(r.GetString())
}
}
Java
Install the Gremlin Java Driver:
The example uses Maven. You need to add the following dependencies to the pom.xml file of your project.
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>gremlin-core</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>gremlin-driver</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>gremlin-server</artifactId>
<version>3.6.0</version>
</dependency>
The following is an example of connecting to PuppyGraph and executing a query.
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
public class Example {
public static void main(String[] args) throws Exception {
// Connect to PuppyGraph database using a Gremlin Server.
Cluster cluster = Cluster.build().addContactPoint("localhost").create();
Client client = cluster.connect();
// Create a graph traversal source for executing queries.
GraphTraversalSource g = AnonymousTraversalSource.traversal()
.withRemote(DriverRemoteConnection.using(client, "g"));
// Get all nodes (vertices) in the graph.
System.out.println("All nodes (vertices) in the graph:");
System.out.println(g.V().valueMap().toList());
System.out.println();
// Close all opened resources to free up system resources.
g.close();
client.close();
cluster.close();
}
}
Javascript
Install the Gremlin Javascript Driver:
The following is an example of connecting to PuppyGraph and executing a query.
const gremlin = require('gremlin');
const Graph = gremlin.structure.Graph;
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
// Establish a connection to the graph database using Gremlin Server.
const endpoint = 'ws://localhost:8182/gremlin';
const graph = new Graph();
const connection = new DriverRemoteConnection(endpoint);
const g = graph.traversal().withRemote(connection);
// Get all nodes (vertices) from the graph.
g.V().toList()
.then(data => {
console.log(data);
return connection.close();
})
.then(() => {
console.log('Connection closed');
})
.catch(error => {
console.error('Error:', error);
connection.close();
});
The following link is a code repository where you can learn more examples of gremlin queries.
Client Applications
Gremlin Console (Apache TinkerPop)
PuppyGraph also supports the Gremlin Console provided by Apache TinkerPop.
You need to modify the remote.yaml file in the conf directory to use the IP and port (by default 8182) of the PuppyGraph server.
In the Gremlin Console, run the following commands to connect to PuppyGraph:
Gremlin Functions Reference
This document describes the TinkerPop Gremlin functions that are fully supported by PuppyGraph, as well as custom Gremlin functions and predicates that extend the standard Gremlin API.
TinkerPop Gremlin Functions
PuppyGraph provides comprehensive support for TinkerPop Gremlin query language.
Graph Navigation Steps
These steps are used to navigate the graph structure, starting from vertices or edges and traversing relationships.
| Step | Description | Example |
|---|---|---|
V() |
Get all vertices or specific vertices by ID | g.V(), g.V('person[v1]', 'person[v4]') |
E() |
Get all edges or specific edges by ID | g.E(), g.E('knows[e7]', 'created[e9]') |
out() |
Traverse to outgoing adjacent vertices | g.V('person[v1]').out(), g.V('person[v1]').out('knows') |
in() |
Traverse to incoming adjacent vertices | g.V('software[v3]').in(), g.V('software[v3]').in('created') |
both() |
Traverse to both incoming and outgoing adjacent vertices | g.V('person[v1]').both(), g.V('person[v1]').both('knows') |
outE() |
Traverse to outgoing edges | g.V('person[v1]').outE(), g.V('person[v1]').outE('created') |
inE() |
Traverse to incoming edges | g.V('software[v3]').inE(), g.V('software[v3]').inE('created') |
bothE() |
Traverse to both incoming and outgoing edges | g.V('person[v1]').bothE(), g.V('person[v1]').bothE('created') |
outV() |
Traverse from edge to its outgoing (source) vertex | g.E('knows[e7]').outV() |
inV() |
Traverse from edge to its incoming (target) vertex | g.E('knows[e7]').inV() |
bothV() |
Traverse from edge to both vertices | g.E('knows[e7]').bothV() |
otherV() |
Traverse from edge to the other vertex (opposite of current) | g.V('person[v1]').outE().otherV() |
Filtering Steps
These steps filter the traversal stream based on specified conditions.
| Step | Description | Example |
|---|---|---|
has() |
Filter by property existence or value | g.V().has('name', 'marko'), g.V().has('age', gt(30)) |
hasLabel() |
Filter by element label | g.V().hasLabel('person') |
hasId() |
Filter by element ID | g.V().hasId('person[v1]', 'person[v4]', 'person[v6]') |
hasKey() |
Filter by property key existence | g.V().hasKey('name') |
hasValue() |
Filter by property value | g.V().hasValue('marko') |
and() |
Logical AND of multiple traversals | g.V().and(has('age', gt(30)), has('name', 'marko')) |
or() |
Logical OR of multiple traversals | g.V().or(has('age', gt(30)), has('name', 'marko')) |
not() |
Logical negation of a traversal | g.V().not(has('age', gt(30))) |
where() |
Filter based on predicate or traversal | g.V().where(out().count().is(gt(2))) |
filter() |
Generic filter step with lambda or traversal | g.V().filter(out().count().is(gt(2))) |
dedup() |
Remove duplicate elements from the stream | g.V().out().out().dedup() |
range() |
Limit results to a range | g.V().range(0, 10) |
limit() |
Limit number of results | g.V().limit(10) |
skip() |
Skip a number of results | g.V().skip(10) |
Aggregation Steps
These steps aggregate and reduce the traversal stream to produce summary results.
| Step | Description | Example |
|---|---|---|
count() |
Count the number of elements | g.V().count(), g.V().out().count() |
sum() |
Sum numeric property values | g.V().values('age').sum() |
min() |
Find minimum value | g.V().values('age').min() |
max() |
Find maximum value | g.V().values('age').max() |
mean() |
Calculate average value | g.V().values('age').mean() |
fold() |
Collect all elements into a list | g.V().values('name').fold() |
group() |
Group elements by key | g.V().group().by('name').by(count()) |
groupCount() |
Count grouped elements | g.V().groupCount().by('name') |
Mapping Steps
These steps transform elements in the traversal stream.
| Step | Description | Example |
|---|---|---|
values() |
Extract property values | g.V().values('name') |
valueMap() |
Get property key-value map | g.V().valueMap() |
properties() |
Get property objects | g.V().properties('name') |
id() |
Get element ID | g.V().id() |
label() |
Get element label | g.V().label() |
project() |
Project multiple properties into a map | g.V().project('id', 'name').by(id()).by('name') |
select() |
Select previously labeled steps | g.V().as('a').out().as('b').select('a', 'b') |
unfold() |
Unroll a collection into individual elements | g.V().values('tags').unfold() |
path() |
Get the full path of the traversal | g.V('person[v1]').out().out().path() |
math() |
Perform mathematical operations | g.V().values('age').math('_ * 2') |
map() |
Generic map transformation | g.V().map(out().count()) |
order() |
Order/sort results | g.V().order().by('age', desc) |
Control Flow Steps
These steps control the flow of traversal execution.
| Step | Description | Example |
|---|---|---|
repeat() |
Repeat a traversal pattern | g.V('person[v1]').repeat(out()).times(3) |
until() |
Repeat until condition is met | g.V('person[v1]').repeat(out()).until(has('name', 'josh')) |
emit() |
Emit intermediate results during repeat | g.V('person[v1]').repeat(out()).emit() |
union() |
Execute multiple traversals in parallel | g.V().union(out('knows'), out('created')) |
coalesce() |
Return first non-empty traversal result | g.V().coalesce(out('knows'), out('created')) |
choose() / branch() |
Conditional branching based on predicate | g.V().choose(has('age', gt(30)), out('knows'), out('created')) |
Custom Gremlin Functions
PuppyGraph extends the standard Gremlin API with custom functions and predicates to provide enhanced querying capabilities.
ArrayP Predicates
Predicates for filtering based on array/collection properties.
| Method | Description | Example |
|---|---|---|
ArrayP.containsAll(values...) |
Tests if array contains all specified values | g.V().has('tags', ArrayP.containsAll('java', 'graph')) |
ArrayP.containsAny(values...) |
Tests if array contains any of the specified values | g.V().has('tags', ArrayP.containsAny('java', 'python')) |
ArrayP.exact(values...) |
Tests if array exactly equals the specified values (order matters) | g.V().has('sequence', ArrayP.exact(1, 2, 3)) |
ArrayP.exactAnyOrder(values...) |
Tests if array equals the specified values (order doesn't matter) | g.V().has('tags', ArrayP.exactAnyOrder('graph', 'java')) |
ArrayP.allSatisfy(predicate) |
Tests if all array elements satisfy the given predicate | g.V().has('scores', ArrayP.allSatisfy(P.gt(80))) |
ArrayP.anySatisfy(predicate) |
Tests if any array element satisfies the given predicate | g.V().has('scores', ArrayP.anySatisfy(P.gt(90))) |
Example Usage:
// Find vertices with tags containing both 'java' and 'graph'
g.V().has('tags', ArrayP.containsAll('java', 'graph'))
// Find vertices where tags contain any of 'graph' or 'ai'
g.V().has('tags', ArrayP.containsAny('graph', 'ai'))
// Find vertices where all scores are greater than 80
g.V().has('scores', ArrayP.allSatisfy(P.gt(80)))
// Find vertices where any score is greater than 90
g.V().has('scores', ArrayP.anySatisfy(P.gt(90)))
PuppyTextP Predicates
Text-based predicates for string matching and filtering.
Available Methods:
| Method | Description | Example |
|---|---|---|
PuppyTextP.containingCaseInsensitive(value) |
Case-insensitive substring matching | g.V().has('name', PuppyTextP.containingCaseInsensitive('lop')) |
PuppyTextP.notContainingCaseInsensitive(value) |
Negation of case-insensitive substring matching | g.V().has('name', PuppyTextP.notContainingCaseInsensitive('marko')) |
PuppyTextP.regex(pattern) |
Regular expression matching | g.V().has('name', PuppyTextP.regex('^j.*')) |
PuppyTextP.notRegex(pattern) |
Negation of regular expression matching | g.V().has('name', PuppyTextP.notRegex('^p.*')) |
Example Usage: