Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ jobs:
command: mvn test -Dtest.graph.type=<<parameters.graphType>> <<parameters.args>>
- store_cache

test-demo:
executor: 'j21'
steps:
- timeout
- checkout
- setup_remote_docker
- start-db
- load_cache
- install
- run:
name: Run demo
command: mvn compile exec:java -Dexec.mainClass="org.example.Main"
working_directory: demo
- store_cache

test-console:
executor: 'j21'
steps:
Expand Down Expand Up @@ -252,6 +267,12 @@ workflows:
jobs:
- test-plugin

test-demo:
when:
not: <<pipeline.parameters.docker-img>>
jobs:
- test-demo

deploy:
jobs:
- deploy:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ gremlin:
- "e1:[a]->[b]"
- "e2:[b,c]->[e,f]"
driver:
hosts: [ "127.0.0.1:8529" ]
hosts: [ "172.28.0.1:8529" ]
password: test
```

Expand Down
5 changes: 5 additions & 0 deletions demo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
<artifactId>arangodb-tinkerpop-provider</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
</dependencies>

<repositories>
Expand Down
235 changes: 222 additions & 13 deletions demo/src/main/java/org/example/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,247 @@

package org.example;

import com.arangodb.ArangoDB;
import com.arangodb.ArangoDatabase;
import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph;
import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder;
import org.apache.commons.configuration2.Configuration;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;

import java.util.List;
import java.util.Map;

import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc;
import static org.apache.tinkerpop.gremlin.process.traversal.Scope.local;
import static org.apache.tinkerpop.gremlin.structure.Column.values;

/**
* Demo program showing how to use Gremlin with ArangoDB TinkerPop provider.
* Based on: Practical Gremlin: An Apache TinkerPop Tutorial by Kelvin R. Lawrence
* (<a href=https://www.kelvinlawrence.net/book/PracticalGremlin.html>link</a>)
*/
public class Main {
private static final String DB_NAME = "demo";
private static final String GRAPHML_FILE = "src/main/resources/air-routes-small.graphml";

@SuppressWarnings("resource")
public static void main(String[] args) {
System.out.println("Starting ArangoDB TinkerPop Demo with Air Routes Data");

// create Tinkerpop Graph backed by ArangoDB
//region cleanup
System.out.println("Cleaning up existing database:");
{
ArangoDatabase db = new ArangoDB.Builder()
.host("172.28.0.1", 8529)
.password("test")
.build()
.db(DB_NAME);
if (db.exists()) {
db.drop();
}
db.arango().shutdown();
}
//endregion

// create Tinkerpop graph backed by ArangoDB
Configuration conf = new ArangoDBConfigurationBuilder()
.hosts("127.0.0.1:8529")
.hosts("172.28.0.1:8529")
.user("root")
.password("test")
.database(DB_NAME)
.enableDataDefinition(true)
.build();
ArangoDBGraph g = ArangoDBGraph.open(conf);
ArangoDBGraph graph = ArangoDBGraph.open(conf);
GraphTraversalSource g = graph.traversal();

// print supported features
System.out.println(g.features());
System.out.println("Graph Features:");
System.out.println(graph.features());

// Import GraphML data
System.out.println("\nImporting Air Routes data from GraphML file...");
{
g.io(Main.GRAPHML_FILE).read().iterate();
System.out.println("Data import completed.");
}

//region Basic Gremlin Queries
System.out.println("\n=== Basic Gremlin Queries ===");
{
System.out.println("Counting vertices and edges:");
long vertexCount = g.V().count().next();
long edgeCount = g.E().count().next();
System.out.println(" Vertices: " + vertexCount);
System.out.println(" Edges: " + edgeCount);

System.out.println("\nVertex labels in the graph:");
g.V().label().dedup().forEachRemaining(label -> System.out.println(" " + label));

System.out.println("\nEdge labels in the graph:");
g.E().label().dedup().forEachRemaining(label -> System.out.println(" " + label));

System.out.println("\nSample of 5 airports:");
g.V().hasLabel("airport").limit(5).valueMap().forEachRemaining(
vm -> System.out.println(" " + vm)
);

System.out.println("\nSample of 5 routes:");
g.E().hasLabel("route").limit(5).valueMap().forEachRemaining(
vm -> System.out.println(" " + vm)
);
}
//endregion

//region Explore airports and routes
System.out.println("\n=== Exploring Airports and Routes ===");
{
// Count airports by region
System.out.println("\nTop 5 regions by number of airports:");
g.V().hasLabel("airport")
.groupCount().by("region")
.order(local).by(values, desc)
.<Map.Entry<String, Long>>unfold().limit(5)
.forEachRemaining(e ->
System.out.println(" " + e.getKey() + ": " + e.getValue() + " airports"));

// write vertex
Vertex v = g.addVertex("person");
v.property("name", "Joe");
v.property("age", 22);

// read vertex
Vertex joe = g.traversal().V().has("name", "Joe").next();
System.out.println("Joe's age: " + joe.property("age").value());
// Find airports with most routes
System.out.println("\nTop 5 airports with most outgoing routes:");
g.V().hasLabel("airport")
.project("airport", "code", "routes")
.by("city")
.by("code")
.by(__.outE("route").count())
.order().by("routes", desc)
.limit(5)
.forEachRemaining(m ->
System.out.println(" " + m.get("airport") + " (" + m.get("code") + "): " + m.get("routes") + " routes"));
}
//endregion

g.close();
//region Graph Algorithms
System.out.println("\n=== Graph Algorithms ===");
{
// Degree centrality - find most connected airports
System.out.println("\nTop 5 airports by degree centrality (most connections):");
g.V().hasLabel("airport")
.project("airport", "code", "degree")
.by("city")
.by("code")
.by(__.bothE().count())
.order().by("degree", desc)
.limit(5)
.forEachRemaining(m ->
System.out.println(" " + m.get("airport") + " (" + m.get("code") + "): " + m.get("degree") + " connections"));

// Find a path between two airports
System.out.println("\nFinding shortest path between Boston (BOS) and Atlanta (ATL):");

g.V().hasLabel("airport")
.has("code", "BOS")
.repeat(__.out().simplePath())
.until(__.has("code", "ATL"))
.limit(5)
.path().by("code")
.forEachRemaining(path ->
System.out.println(" Path (" + (path.size() - 1) + " hops): " +
String.join(" -> ", path.objects().stream().map(Object::toString).toList())));

// Find all airports reachable within 2 hops from London
System.out.println("\nCount of the airports reachable within 2 hops from Boston (BOS):");
Long count = g.V().has("code", "BOS")
.repeat(__.out().simplePath())
.times(2)
.dedup()
.count()
.next();
System.out.println(" " + count + " airports");
}
//endregion

//region AQL Queries
System.out.println("\n=== AQL Queries ===");
{
// Find weighted k-shortest paths between two airports with an AQL query
System.out.println("\nFinding weighted k-shortest paths between Boston (BOS) and San Francisco (SFO) with AQL query:");

String shortestPathQuery = """
LET start = FIRST(
FOR d IN tinkerpop_vertex
FILTER d.code == @start
RETURN d
)

LET target = FIRST(
FOR d IN tinkerpop_vertex
FILTER d.code == @target
RETURN d
)

FOR path IN OUTBOUND K_SHORTEST_PATHS start TO target GRAPH tinkerpop
OPTIONS { weightAttribute: 'dist' }
LIMIT 5
RETURN {
path: path.vertices[*].code,
dist: path.weight
}
""";

graph.<Map<String, ?>>aql(shortestPathQuery, Map.of(
"start", "BOS",
"target", "SFO"
)).forEachRemaining(path ->
System.out.println(" Path (dist: " + path.get("dist") + "): \t" + path.get("path")));

// AQL traversal to find paths between two airports with constraints on edges
System.out.println("\nFinding path between Boston (BOS) and Atlanta (ATL) with max 400 km flights with AQL query:");

String traversalQuery = """
LET start = FIRST(
FOR d IN tinkerpop_vertex
FILTER d.code == @start
RETURN d
)

FOR v,e,p IN 1..10 OUTBOUND start GRAPH tinkerpop
PRUNE cond = e.dist > 400
OPTIONS { uniqueVertices: 'path', order: 'bfs' }
FILTER NOT cond
FILTER v.code == @target
LIMIT 5
RETURN p
""";

graph.<Map<String, ?>>aql(traversalQuery, Map.of(
"start", "BOS",
"target", "ATL"
))
.project("path", "distances", "tot")
.by(__.select("vertices").unfold().values("code").fold())
.by(__.select("edges").unfold().values("dist").fold())
.by(__.select("edges").unfold().values("dist").sum())
.forEachRemaining(it -> {
@SuppressWarnings("unchecked")
List<String> path = (List<String>) it.get("path");
@SuppressWarnings("unchecked")
List<Number> distances = (List<Number>) it.get("distances");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < distances.size(); i++) {
sb
.append(path.get(i))
.append(" -(")
.append(distances.get(i))
.append(")-> ");
}
sb.append(path.getLast());
System.out.println(" Path (dist: " + it.get("tot") + "): \t" + sb);
}
);
}
//endregion

graph.close();
}

}
Loading