This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 1f6a462ac1549cab0b11834d79348717698fe259 Author: Stephen Mallette <[email protected]> AuthorDate: Wed Aug 26 16:12:53 2026 +0000 Add Air Routes to the Sample Data docs book Documents the air routes graph (schema in GQL Graph Types plus runnable examples) and registers an 'airroutes' executable-block graph alias so its examples run against TinkerFactory.createAirRoutes() at build time. Assisted-by: Claude Code:claude-opus-4-8 --- docs/src/data/air-routes.asciidoc | 145 +++++++++++++++++++++ docs/src/data/index.asciidoc | 2 + .../tinkerpop/tinkeradoc/GremlinTreeprocessor.java | 1 + .../tinkeradoc/GremlinTreeprocessorTest.java | 14 ++ 4 files changed, 162 insertions(+) diff --git a/docs/src/data/air-routes.asciidoc b/docs/src/data/air-routes.asciidoc new file mode 100644 index 0000000000..20caa0cf8d --- /dev/null +++ b/docs/src/data/air-routes.asciidoc @@ -0,0 +1,145 @@ +//// +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +//// +[[air-routes]] +[llms-summary="The air routes graph: a large, real-world graph of airports connected by flight routes and grouped by geography. Includes its GQL Graph Types schema and runnable Gremlin examples that exercise property lookups, edge-weight ranking and multi-hop connectivity."] +== The Air Routes Graph + +The air routes graph models the world's commercial air travel network. Its `airport` vertices are +connected by `route` edges that represent a nonstop flight between two airports, and each `route` +carries a `dist` property with the distance of that flight. Alongside the airports, the graph +records geography as `continent` and `country` vertices, and a `contains` edge links a continent or +a country to the airports that lie within it. A single `version` vertex holds metadata about the +dataset itself. + +The dataset has long been used to showcase and teach Gremlin and was popularized by the first +edition of link:https://kelvinlawrence.net/book/PracticalGremlin.html[Practical Gremlin]. With 3749 +vertices and 57645 edges, it is far larger than the other sample graphs, and its structure reflects +the real world rather than a hand-built illustration. That scale and realism make it the best choice +for demonstrating traversals that resemble genuine questions: looking up a record by a business key, +ranking routes by distance, measuring how well connected an airport is, and reasoning about journeys +that require a connection. The graph is created with `TinkerFactory.createAirRoutes()` and ships as +`data/air-routes.*`. It is too large to depict as a single diagram. + +=== Schema + +[source,gql] +---- +-- node types +(:airport => { code :: STRING NOT NULL, icao :: STRING, desc :: STRING, type :: STRING, city :: STRING, region :: STRING, country :: STRING, runways :: INT, longest :: INT, elev :: INT, lat :: DOUBLE, lon :: DOUBLE }), +(:country => { code :: STRING, desc :: STRING, type :: STRING }), +(:continent => { code :: STRING, desc :: STRING, type :: STRING }), +(:version => { code :: STRING, desc :: STRING, type :: STRING, author :: STRING, date :: STRING }), + +-- edge types +(:airport)-[:route { dist :: INT }]->(:airport), +(:continent)-[:contains]->(:airport), +(:country)-[:contains]->(:airport) +---- + +An `airport` is identified in traversals by its three-letter IATA `code`, such as `IAD` for +Washington Dulles, and its `desc`, `city`, and geographic coordinates describe where it is. Every +vertex carries a `type` property that names the kind of entity it represents, which is useful when +the same property key appears across labels. The `contains` edge reaches an airport from both its +`continent` and its `country`, so an airport has two incoming `contains` edges. The lone `version` +vertex is a metadata record for the dataset and participates in no routes. + +=== Examples + +A single airport is located by its IATA `code`, and its descriptive properties are read directly +from the vertex. + +[gremlin-groovy,airroutes] +---- +g.V().has('airport','code','IAD'). + valueMap('code','city','region','desc') <1> +---- + +<1> Look up one airport by its `code` and report a few of its descriptive properties. + +The `contains` edges group airports by geography, so the number of airports on each continent is the +count of `contains` edges leaving that continent. + +[gremlin-groovy,airroutes] +---- +g.V().hasLabel('continent'). + project('continent','airports'). <1> + by('desc'). + by(out('contains').hasLabel('airport').count()). + order().by(select('airports'),desc) <2> +---- + +<1> For each continent, project its name and the number of airports it `contains`. +<2> Order the continents from the most airports to the fewest. + +The number of `route` edges leaving an airport is the count of destinations reachable nonstop from +it, which is a simple measure of how busy the airport is. + +[gremlin-groovy,airroutes] +---- +g.V().hasLabel('airport'). + order().by(outE('route').count(),desc). <1> + limit(5). + project('code','routes'). <2> + by('code'). + by(outE('route').count()) +---- + +<1> Order airports by their number of outgoing `route` edges. +<2> Report the `code` of the five busiest airports together with that route count. + +Because each `route` carries a `dist` property, the outgoing routes of an airport can be ranked to +find its longest nonstop flights. + +[gremlin-groovy,airroutes] +---- +g.V().has('airport','code','LHR'). + outE('route'). <1> + order().by('dist',desc). + limit(5). + project('to','dist'). <2> + by(inV().values('code')). + by('dist') +---- + +<1> Step onto the outgoing `route` edges of London Heathrow so each edge's `dist` is available for +ordering. +<2> Rank the routes by `dist` and report the destination `code` and distance of the five longest +nonstop routes. + +A connection query follows two `route` steps to find airports that cannot be reached nonstop from a +starting airport but are reachable with a single stop, then ranks them by how many one-stop routings +lead there. + +[gremlin-groovy,airroutes] +---- +g.V().has('airport','code','AUS').as('origin'). + out('route').aggregate('direct'). <1> + out('route'). + where(without('direct')). + where(neq('origin')). <2> + groupCount().by('code'). + order(local).by(values,desc). + limit(local,5) <3> +---- + +<1> Gather the airports reachable nonstop from Austin into a side collection named `direct`. +<2> Take a second `route` step and keep only airports that are neither nonstop destinations nor +Austin itself, leaving those that require exactly one connection. +<3> Count how many one-stop routings reach each of those airports and keep the five reached by the +most, giving the best-connected onward hubs from Austin. diff --git a/docs/src/data/index.asciidoc b/docs/src/data/index.asciidoc index 61acb5603e..33ba3f56e6 100644 --- a/docs/src/data/index.asciidoc +++ b/docs/src/data/index.asciidoc @@ -57,3 +57,5 @@ include::modern.asciidoc[] include::the-crew.asciidoc[] include::grateful-dead.asciidoc[] + +include::air-routes.asciidoc[] diff --git a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessor.java b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessor.java index 0106f33dba..e117118390 100644 --- a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessor.java +++ b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessor.java @@ -64,6 +64,7 @@ public class GremlinTreeprocessor extends Treeprocessor { m.put("crew", "graph = TinkerFactory.createTheCrew()"); m.put("theCrew", "graph = TinkerFactory.createTheCrew()"); m.put("grateful", "graph = TinkerFactory.createGratefulDead()"); + m.put("airroutes", "graph = TinkerFactory.createAirRoutes()"); m.put("sink", "graph = TinkerFactory.createKitchenSink()"); GRAPH_INIT = Collections.unmodifiableMap(m); } diff --git a/docs/tinkeradoc-extension/src/test/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessorTest.java b/docs/tinkeradoc-extension/src/test/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessorTest.java index 209e73f66e..40ff4968e8 100644 --- a/docs/tinkeradoc-extension/src/test/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessorTest.java +++ b/docs/tinkeradoc-extension/src/test/java/org/apache/tinkerpop/tinkeradoc/GremlinTreeprocessorTest.java @@ -282,6 +282,20 @@ public class GremlinTreeprocessorTest { } } + @Test + public void shouldHandleAirRoutesGraph() { + final RecordingExecutor executor = new RecordingExecutor("==>result"); + final GremlinTreeprocessor processor = new GremlinTreeprocessor(executor); + + try (final Asciidoctor asciidoctor = Asciidoctor.Factory.create()) { + asciidoctor.unregisterAllExtensions(); + asciidoctor.javaExtensionRegistry().treeprocessor(processor); + final String input = "= Test\n\n[gremlin-groovy,airroutes]\n----\ng.V()\n----\n"; + asciidoctor.convert(input, Options.builder().build()); + assertThat(executor.statements.contains("graph = TinkerFactory.createAirRoutes()"), is(true)); + } + } + @Test public void shouldHandleSinkGraph() { final RecordingExecutor executor = new RecordingExecutor("==>result");
