Gremlin is a language/framework for traversing and querying graph databases. The original implementation is written in Groovy, and you can read all about it here: http://gremlin.tinkerpop.com. clj-gremlin is an implementation of Gremlin for Clojure. We try to keep as close as possible to the Groovy implementation, so that you can read the original documentation and apply those lessons to the Clojure implementation. That said, there are a few things that will be different, and this page will document them.
clj-gremlin is available through Clojars, so in order to use it you can simply add this to your lein project.xml file:
:dependencies [[clj-gremlin "0.0.3"]]
In order to get access to all the steps and helper functions, use the clj-gremlin.core namespace:
When showing examples, we will be using the variable g to stand for the current Graph. This should be an instance of the Graph interface from http://blueprints.tinkerpop.com. Most of the examples use the threading operator -> to make them read nicely. The return value of most query steps will be pipelines, which are all Iterable. This means they can be treated as seqable as well, from a clojure standpoint.
Getting all the vertices from the graph is as simple as
(-> g V)
Same thing to get all the edges:
(-> g E)
To get a specific vertice or edge with an id:
(-> g (v 42))
(-> g (e 13))
In order to add more operations, just put them as function calls in the threading operator:
(-> g (v 42) (out "knows") inV)
There are two ways to get properties in clj-gremlin. Neither matches exactly to the way the Groovy version does it. In order to extract a property as part of a pipeline, you just use a symbol as part of the pipeline chain:
(-> g V :name)
However, if you are dealing with a single element, you need to instead use the prop function:
(-> g V (step #(prop % :name))
In order to extract all the properties as part of a chain, the original implementation of Gremlin uses the method map. However, in Clojure, we can't really take that word, since it's used for other things in Clojure. So instead of map, we use props:
(-> g V props)
Since memoize is already a clojure.core function, memoize becomes memo in Gremlin.
Filter is also one of those very common operations in Clojure, so the name for "filter" is "where" in the Clojure implementation.
Square brackets is pretty clunky for use as a function in Clojure. Instead, this implementation just uses at.
(-> g V (at 2))
It takes two arguments for the range version:
(-> g V (at 2 4))
Loop is another already used word in Clojure. looping became the translation, although it feels pretty clunky. Better alternatives would be great!
In this case, the natural thing would have been to create a group-by function. However, that one is taken and well used, so grouping is done with "group" instead.
The order step takes a required closure. In the Groovy implementation it yields a Pair object to the closure. In the Clojure version I instead decided to yield the two objects as separate arguments. The same thing happens with the potential second closure given to group-count.
Because Clojure usually doesn't use camel cased names, but instead separates names with dashes, several functions got a change in name for the sake of consistency:
- hasNot -> has-not
- sideEffect -> side-effect
- groupCount -> group-count
- ifThenElse -> if-then-else
- copySplit -> copy-split
- fairMerge -> fair-merge
- exhaustMerge -> exhaust-merge
- simplePath -> simple-path
- enablePath -> enable-path