1616
1717package org .example ;
1818
19+ import com .arangodb .ArangoDB ;
20+ import com .arangodb .ArangoDatabase ;
1921import com .arangodb .tinkerpop .gremlin .structure .ArangoDBGraph ;
2022import com .arangodb .tinkerpop .gremlin .utils .ArangoDBConfigurationBuilder ;
2123import org .apache .commons .configuration2 .Configuration ;
22- import org .apache .tinkerpop .gremlin .structure .Vertex ;
24+ import org .apache .tinkerpop .gremlin .process .traversal .dsl .graph .GraphTraversalSource ;
25+ import org .apache .tinkerpop .gremlin .process .traversal .dsl .graph .__ ;
2326
27+ import java .util .List ;
28+ import java .util .Map ;
29+
30+ import static org .apache .tinkerpop .gremlin .process .traversal .Order .desc ;
31+ import static org .apache .tinkerpop .gremlin .process .traversal .Scope .local ;
32+ import static org .apache .tinkerpop .gremlin .structure .Column .values ;
33+
34+ /**
35+ * Demo program showing how to use Gremlin with ArangoDB TinkerPop provider.
36+ * Based on: Practical Gremlin: An Apache TinkerPop Tutorial by Kelvin R. Lawrence
37+ * (<a href=https://www.kelvinlawrence.net/book/PracticalGremlin.html>link</a>)
38+ */
2439public class Main {
2540 private static final String DB_NAME = "demo" ;
41+ private static final String GRAPHML_FILE = "src/main/resources/air-routes-small.graphml" ;
2642
43+ @ SuppressWarnings ("resource" )
2744 public static void main (String [] args ) {
45+ System .out .println ("Starting ArangoDB TinkerPop Demo with Air Routes Data" );
2846
29- // create Tinkerpop Graph backed by ArangoDB
47+ //region cleanup
48+ System .out .println ("Cleaning up existing database:" );
49+ {
50+ ArangoDatabase db = new ArangoDB .Builder ()
51+ .host ("172.28.0.1" , 8529 )
52+ .password ("test" )
53+ .build ()
54+ .db (DB_NAME );
55+ if (db .exists ()) {
56+ db .drop ();
57+ }
58+ db .arango ().shutdown ();
59+ }
60+ //endregion
61+
62+ // create Tinkerpop graph backed by ArangoDB
3063 Configuration conf = new ArangoDBConfigurationBuilder ()
31- .hosts ("127.0 .0.1:8529" )
64+ .hosts ("172.28 .0.1:8529" )
3265 .user ("root" )
3366 .password ("test" )
3467 .database (DB_NAME )
3568 .enableDataDefinition (true )
3669 .build ();
37- ArangoDBGraph g = ArangoDBGraph .open (conf );
70+ ArangoDBGraph graph = ArangoDBGraph .open (conf );
71+ GraphTraversalSource g = graph .traversal ();
3872
3973 // print supported features
40- System .out .println (g .features ());
74+ System .out .println ("Graph Features:" );
75+ System .out .println (graph .features ());
76+
77+ // Import GraphML data
78+ System .out .println ("\n Importing Air Routes data from GraphML file..." );
79+ {
80+ g .io (Main .GRAPHML_FILE ).read ().iterate ();
81+ System .out .println ("Data import completed." );
82+ }
83+
84+ //region Basic Gremlin Queries
85+ System .out .println ("\n === Basic Gremlin Queries ===" );
86+ {
87+ System .out .println ("Counting vertices and edges:" );
88+ long vertexCount = g .V ().count ().next ();
89+ long edgeCount = g .E ().count ().next ();
90+ System .out .println (" Vertices: " + vertexCount );
91+ System .out .println (" Edges: " + edgeCount );
92+
93+ System .out .println ("\n Vertex labels in the graph:" );
94+ g .V ().label ().dedup ().forEachRemaining (label -> System .out .println (" " + label ));
95+
96+ System .out .println ("\n Edge labels in the graph:" );
97+ g .E ().label ().dedup ().forEachRemaining (label -> System .out .println (" " + label ));
98+
99+ System .out .println ("\n Sample of 5 airports:" );
100+ g .V ().hasLabel ("airport" ).limit (5 ).valueMap ().forEachRemaining (
101+ vm -> System .out .println (" " + vm )
102+ );
103+
104+ System .out .println ("\n Sample of 5 routes:" );
105+ g .E ().hasLabel ("route" ).limit (5 ).valueMap ().forEachRemaining (
106+ vm -> System .out .println (" " + vm )
107+ );
108+ }
109+ //endregion
110+
111+ //region Explore airports and routes
112+ System .out .println ("\n === Exploring Airports and Routes ===" );
113+ {
114+ // Count airports by region
115+ System .out .println ("\n Top 5 regions by number of airports:" );
116+ g .V ().hasLabel ("airport" )
117+ .groupCount ().by ("region" )
118+ .order (local ).by (values , desc )
119+ .<Map .Entry <String , Long >>unfold ().limit (5 )
120+ .forEachRemaining (e ->
121+ System .out .println (" " + e .getKey () + ": " + e .getValue () + " airports" ));
41122
42- // write vertex
43- Vertex v = g .addVertex ("person" );
44- v .property ("name" , "Joe" );
45- v .property ("age" , 22 );
46123
47- // read vertex
48- Vertex joe = g .traversal ().V ().has ("name" , "Joe" ).next ();
49- System .out .println ("Joe's age: " + joe .property ("age" ).value ());
124+ // Find airports with most routes
125+ System .out .println ("\n Top 5 airports with most outgoing routes:" );
126+ g .V ().hasLabel ("airport" )
127+ .project ("airport" , "code" , "routes" )
128+ .by ("city" )
129+ .by ("code" )
130+ .by (__ .outE ("route" ).count ())
131+ .order ().by ("routes" , desc )
132+ .limit (5 )
133+ .forEachRemaining (m ->
134+ System .out .println (" " + m .get ("airport" ) + " (" + m .get ("code" ) + "): " + m .get ("routes" ) + " routes" ));
135+ }
136+ //endregion
50137
51- g .close ();
138+ //region Graph Algorithms
139+ System .out .println ("\n === Graph Algorithms ===" );
140+ {
141+ // Degree centrality - find most connected airports
142+ System .out .println ("\n Top 5 airports by degree centrality (most connections):" );
143+ g .V ().hasLabel ("airport" )
144+ .project ("airport" , "code" , "degree" )
145+ .by ("city" )
146+ .by ("code" )
147+ .by (__ .bothE ().count ())
148+ .order ().by ("degree" , desc )
149+ .limit (5 )
150+ .forEachRemaining (m ->
151+ System .out .println (" " + m .get ("airport" ) + " (" + m .get ("code" ) + "): " + m .get ("degree" ) + " connections" ));
152+
153+ // Find a path between two airports
154+ System .out .println ("\n Finding shortest path between Boston (BOS) and Atlanta (ATL):" );
155+
156+ g .V ().hasLabel ("airport" )
157+ .has ("code" , "BOS" )
158+ .repeat (__ .out ().simplePath ())
159+ .until (__ .has ("code" , "ATL" ))
160+ .limit (5 )
161+ .path ().by ("code" )
162+ .forEachRemaining (path ->
163+ System .out .println (" Path (" + (path .size () - 1 ) + " hops): " +
164+ String .join (" -> " , path .objects ().stream ().map (Object ::toString ).toList ())));
165+
166+ // Find all airports reachable within 2 hops from London
167+ System .out .println ("\n Count of the airports reachable within 2 hops from Boston (BOS):" );
168+ Long count = g .V ().has ("code" , "BOS" )
169+ .repeat (__ .out ().simplePath ())
170+ .times (2 )
171+ .dedup ()
172+ .count ()
173+ .next ();
174+ System .out .println (" " + count + " airports" );
175+ }
176+ //endregion
177+
178+ //region AQL Queries
179+ System .out .println ("\n === AQL Queries ===" );
180+ {
181+ // Find weighted k-shortest paths between two airports with an AQL query
182+ System .out .println ("\n Finding weighted k-shortest paths between Boston (BOS) and San Francisco (SFO) with AQL query:" );
183+
184+ String shortestPathQuery = """
185+ LET start = FIRST(
186+ FOR d IN tinkerpop_vertex
187+ FILTER d.code == @start
188+ RETURN d
189+ )
190+
191+ LET target = FIRST(
192+ FOR d IN tinkerpop_vertex
193+ FILTER d.code == @target
194+ RETURN d
195+ )
196+
197+ FOR path IN OUTBOUND K_SHORTEST_PATHS start TO target GRAPH tinkerpop
198+ OPTIONS { weightAttribute: 'dist' }
199+ LIMIT 5
200+ RETURN {
201+ path: path.vertices[*].code,
202+ dist: path.weight
203+ }
204+ """ ;
205+
206+ graph .<Map <String , ?>>aql (shortestPathQuery , Map .of (
207+ "start" , "BOS" ,
208+ "target" , "SFO"
209+ )).forEachRemaining (path ->
210+ System .out .println (" Path (dist: " + path .get ("dist" ) + "): \t " + path .get ("path" )));
211+
212+ // AQL traversal to find paths between two airports with constraints on edges
213+ System .out .println ("\n Finding path between Boston (BOS) and Atlanta (ATL) with max 400 km flights with AQL query:" );
214+
215+ String traversalQuery = """
216+ LET start = FIRST(
217+ FOR d IN tinkerpop_vertex
218+ FILTER d.code == @start
219+ RETURN d
220+ )
221+
222+ FOR v,e,p IN 1..10 OUTBOUND start GRAPH tinkerpop
223+ PRUNE cond = e.dist > 400
224+ OPTIONS { uniqueVertices: 'path', order: 'bfs' }
225+ FILTER NOT cond
226+ FILTER v.code == @target
227+ LIMIT 5
228+ RETURN p
229+ """ ;
230+
231+ graph .<Map <String , ?>>aql (traversalQuery , Map .of (
232+ "start" , "BOS" ,
233+ "target" , "ATL"
234+ ))
235+ .project ("path" , "distances" , "tot" )
236+ .by (__ .select ("vertices" ).unfold ().values ("code" ).fold ())
237+ .by (__ .select ("edges" ).unfold ().values ("dist" ).fold ())
238+ .by (__ .select ("edges" ).unfold ().values ("dist" ).sum ())
239+ .forEachRemaining (it -> {
240+ @ SuppressWarnings ("unchecked" )
241+ List <String > path = (List <String >) it .get ("path" );
242+ @ SuppressWarnings ("unchecked" )
243+ List <Number > distances = (List <Number >) it .get ("distances" );
244+ StringBuilder sb = new StringBuilder ();
245+ for (int i = 0 ; i < distances .size (); i ++) {
246+ sb
247+ .append (path .get (i ))
248+ .append (" -(" )
249+ .append (distances .get (i ))
250+ .append (")-> " );
251+ }
252+ sb .append (path .getLast ());
253+ System .out .println (" Path (dist: " + it .get ("tot" ) + "): \t " + sb );
254+ }
255+ );
256+ }
257+ //endregion
258+
259+ graph .close ();
52260 }
261+
53262}
0 commit comments