Ogre is a Clojure wrapper for the Gremlin graph traversal language of Apache TinkerPop™. This documentation assumes that its audience has an existing knowledge of TinkerPop concepts and the Gremlin language syntax.
Orge artifacts are released to Clojars. Maven users should add the following repository definition to your pom.xml:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
With Leiningen:
[clojurewerkz/ogre "3.3.0.0"]
With Maven:
<dependency>
<groupId>clojurewerkz</groupId>
<artifactId>ogre</artifactId>
<version>3.3.0.0</version>
</dependency>
To get an idea of how Ogre helps make Gremlin easier to work with in Clojure, let's convert a basic Gremlin traversal into an Ogre traversal. Consider the following Gremlin traversal written in Java:
g.V().has("name","gremlin"). // Get the vertex with the name "gremlin"
out("knows"). // Traverse to people who Gremlin knows
out("knows"). // Traverse to the people those people know
values("name") // Get the names of those people
In Ogre this traversal is written as:
(traverse g V (has :name "gremlin") ;; Get the vertex with the name "gremlin"
(out :knows) ;; Traverse to people who Gremlin knows
(out :knows) ;; Traverse to the people those people know
(values :name)) ;; Get the names of those people
The flow of Ogre maps quite naturally to that of Gremlin written in Java and this approach is quite typical of Ogre in most cases though there are a few areas of minor step naming differences to be better in line with Clojure.
Gremlin is commonly used in the Gremlin Console which is a Groovy-based REPL. Gremlin is quite at home in that style of environment so this documentation will be make heavy use of the Clojure REPL to explain Ogre usage.
user=> (load "clojurewerkz/ogre/core")
nil
user=> (in-ns 'clojurewerkz.ogre.core)
#object[clojure.lang.Namespace 0x228e9715 "clojurewerkz.ogre.core"]
clojurewerkz.ogre.core=> (import '[org.apache.tinkerpop.gremlin.tinkergraph.structure TinkerGraph TinkerFactory])
org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph
Note that TinkerGraph was imported as a sample graph for purpose of this documentation, but obviously any TinkerPop-enabled graph would suffice.
Unless otherwise noted, the code in the following sections will assume that the REPL was initialized as shown above.
Getting a Graph
instance is the first step to working with Gremlin and
therefore is the first step to working with Ogre. Ogre provides a wrapper around
TinkerPop's GraphFactory
, which provides a provider-agnostic way to create
graphs. The following code will create a TinkerGraph instance with Ogre:
clojurewerkz.ogre.core=> (def graph (open-graph {(Graph/GRAPH) (.getName TinkerGraph)}))
#'clojurewerkz.ogre.core/graph
In the above example open-graph
takes a Map
of arguments which is the configuration
object that GraphFactory
accepts in its openGraph()
method. Obviously, it would also
be possible to simply instantiate the TinkerGraph directly as follows:
clojurewerkz.ogre.core=> (def graph (TinkerGraph/open))
#'clojurewerkz.ogre.core/graph
While the second method is more straightforward it is specific to TinkerGraph. Not all
Graph
instances will be instantiated that way. Unless otherwise noted, this documentation
will focus on traversals that work with the modern graph
which is a small sample dataset that is provided by TinkerPop. This data can be loaded
into the graph
instances as follows:
clojurewerkz.ogre.core=> (TinkerFactory/generateModern graph)
nil
clojurewerkz.ogre.core=> graph
#object[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph 0x6e362190 "tinkergraph[vertices:6 edges:6]"]
For purpose of this documentation and experimentation all of the above could be simplified to:
clojurewerkz.ogre.core=> (def graph (TinkerFactory/createModern)) ;; graph = TinkerFactory.createModern()
#'clojurewerkz.ogre.core/graph
clojurewerkz.ogre.core=> graph
#object[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph 0x75eeb446 "tinkergraph[vertices:6 edges:6]"]
With a Graph
instance and some data in place it is now possible to execute some traversals.
As with Gremlin, Ogre needs a TraversalSource
object. A basic TraversalSource
can be
constructed with:
clojurewerkz.ogre.core=> (def g (traversal graph)) ;; graph.traversal()
#'clojurewerkz.ogre.core/g
The familiar g
becomes the cornerstone of traversing that Gremlin users are familiar with. Consider
the following Gremlin Java based traversal:
g.V().values("name")
The above traversal can be written in Ogre as:
clojurewerkz.ogre.core=> (traverse g V (values "name") (into-seq!))
("marko" "vadas" "lop" "josh" "ripple" "peter")
clojurewerkz.ogre.core=> (traverse g V (values :name) (into-seq!))
("marko" "vadas" "lop" "josh" "ripple" "peter")
The traverse
function takes a TraversalSource
and the sequence of traversal steps that make
up the traversal. There are two important things to note. First, property keys can be specified
as a string, as in "name"
or as a keyword :name
. The latter can be a bit nicer in Clojure.
Second, the statement is concluded with a terminator, in this case (into-seq!)
which iterates
the traversal into a Clojure sequence. There are a number of such terminator functions available
including:
into-seq!
into-list!
into-vec!
into-set!
iterate!
next!
The point with these terminators is that just like Gremlin in any other language, Ogre does
not iterate a Traversal
for you. It is an Iterator
ultimately and it is up to the developer
to decide how it should be treated.
The previous example showed that Ogre can use keywords for property keys, but they can be also be used in other areas as well:
clojurewerkz.ogre.core=> (traverse g V (has :name "marko") (out :created) (values :name) (into-seq!))
("lop")
clojurewerkz.ogre.core=> (traverse g V (has :name "marko") (as :a) (out :knows) (as :b) (select :a :b) (by :name) (into-seq!))
({"a" "marko", "b" "vadas"} {"a" "marko", "b" "josh"})
Gremlin has a number of steps all of which are supported by Ogre. As the documentation of Ogre grows it may come to cover all of those steps, but for now it will be best to look at the Ogre unit tests when syntax isn't clear. As Ogre implements the TinkerPop Process Test Suite, it will always have a complete body of examples to examine.
The version scheme for Ogre is as follows:
[Full TinkerPop version].[Ogre major version]
. Philosophically, releases of
TinkerPop will typically trigger releases in Ogre. Ogre is a mere wrapper
around that body of work and the version scheme acknowledges that dependency
directly. Thus, an Ogre release of 3.2.4.2
, would mean that the current
release uses Gremlin 3.2.4
and has undergone two major versions itself so far
since release.
Please take a moment to tell us what you think about this guide on Twitter or the Titanium mailing list.
Let us know what was unclear or what has not been covered. Reader feedback is key to making the documentation better.