TINKERPOP-786 Added some initial documentation for DSLs
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/50f2dac9 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/50f2dac9 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/50f2dac9 Branch: refs/heads/tp32-glv Commit: 50f2dac9d897d5776a2cdd4ae3a7b56a4ed6339a Parents: e661359 Author: Stephen Mallette <[email protected]> Authored: Tue May 9 13:57:48 2017 -0400 Committer: Stephen Mallette <[email protected]> Committed: Tue May 16 11:01:51 2017 -0400 ---------------------------------------------------------------------- docs/src/reference/the-traversal.asciidoc | 154 +++++++++++++++++++++++++ 1 file changed, 154 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/50f2dac9/docs/src/reference/the-traversal.asciidoc ---------------------------------------------------------------------- diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index 2612309..1be365d 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -2939,3 +2939,157 @@ g.V().outE().inV(). by(). by('name') ---- + +[[dsl]] +Domain Specific Languages +------------------------- + +Gremlin is a link:http://en.wikipedia.org/wiki/Domain-specific_language[domain specific language] (DSL) for traversing +graphs. It operates in the language of vertices, edges and properties. Typically, applications built with Gremlin are +not of the graph domain, but instead model their domain within a graph. For example, the "modern" toy graph models +software and person domain objects with the relationships between them (i.e. a person "knows" another person and a +person "created" software). + +image::tinkerpop-modern.png[width=350] + +An analyst who wanted to find all the people who "marko" knows could write the following Gremlin: + +[source,java] +---- +g.V().hasLabel('person').has('name','marko').out('knows') +---- + +While this method achieves the desired answer, it requires the analyst to traverse the graph in the domain language +of the graph rather than the domain language of the social network. A more natural way for the analyst to write this +traversal might be: + +[source,java] +---- +g.persons('marko').knows() +---- + +In the statement above, the traversal is written in the language of the domain, abstracting away the underlying +graph structure from the query. The two traversal results are equivalent and, indeed, the "Social Network DSL" produces +the same set of traversal steps as the "Graph DSL" thus producing equivalent strategy application and performance +runtimes. + +The following sections explain how to develop application specific DSLs for different <<gremlin-variants,Gremlin Language Variants>>. + +[[gremlin-java-dsl] +Gremlin-Java +~~~~~~~~~~~~ + +Creating a DSL in Java requires the `@GremlinDsl` Java annotation in `gremlin-core`. This annotation should be applied +to a "DSL interface" that extends `GraphTraversal.Admin`. + +[source,java] +---- +@GremlinDsl +public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S, E> { +} +---- + +IMPORTANT: The name of the DSL interface should be suffixed with "TraversalDSL". All characters in the interface name +before that become the "name" of the DSL. + +In this interface, define the methods that the DSL will be composed of: + +[source,java] +---- +@GremlinDsl +public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S, E> { + public default GraphTraversal<S, Vertex> knows(String personName) { + return out("knows").hasLabel("person").has("name", personName); + } + + public default <E2 extends Number> GraphTraversal<S, E2> youngestFriendsAge() { + return out("knows").hasLabel("person").values("age").min(); + } +} +---- + +The `@GremlinDsl` annotation is used by the link:https://docs.oracle.com/javase/8/docs/api/index.html?javax/annotation/processing/Processor.html[Java Annotation Processor] +to generate the boilerplate class structure required to properly use the DSL within the TinkerPop framework. These +classes can be generated and maintained by hand, but it would be time consuming, monotonous and error-prone to do so. +Typically, the Java compilation process is automatically configured to detect annotation processors on the classpath +and will automatically use them when found. If that does not happen, it may be necessary to make configuration changes +to the build to allow for the compilation process to be aware of the following `javax.annotation.processing.Processor` +implementation: + +[source,java] +---- +org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDslProcessor +---- + +The annotation processor will generate several classes for the DSL: + +* `SocialTraversal` - A `Traversal` interface that extends the `SocialTraversalDsl` proxying methods to its underlying +interfaces (such as `GraphTraversal`) to instead return a `SocialTraversal` +* `DefaultSocialTraversal` - A default implementation of `SocialTraversal` (typically not used directly by the user) +* `SocialTraversalSource` - Spawns `DefaultSocialTraversal` instances. + +Using the DSL then just involves telling the `Graph` to use it: + +[source,java] +---- +SocialTraversalSource social = graph.traversal(SocialTraversalSource.class); +social.V().has("name","marko").knows("josh"); +---- + +The `SocialTraversalSource` can also be customized with DSL functions. As an additional step, include a class that +extends from `GraphTraversalSource` and with a name that is suffixed with "TraversalSourceDsl". Include in this class, +any custom methods required by the DSL: + +[source,java] +---- +public class SocialTraversalSourceDsl extends GraphTraversalSource { + + public SocialTraversalSourceDsl(final Graph graph, final TraversalStrategies traversalStrategies) { + super(graph, traversalStrategies); + } + + public SocialTraversalSourceDsl(final Graph graph) { + super(graph); + } + + public GraphTraversal<Vertex, Vertex> persons(String... names) { + GraphTraversalSource clone = this.clone(); + + // Manually add a "start" step for the traversal in this case the equivalent of V(). GraphStep is marked + // as a "start" step by passing "true" in the constructor. + clone.getBytecode().addStep(GraphTraversal.Symbols.V); + GraphTraversal<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone); + traversal.asAdmin().addStep(new GraphStep<>(traversal.asAdmin(), Vertex.class, true)); + + traversal = traversal.hasLabel("person"); + if (names.length > 0) traversal = traversal.has("name", P.within(names)); + + return traversal; + } +} +---- + +Then, back in the `SocialTraversal` interface, update the `GremlinDsl` annotation with the `traversalSource` argument +to point to the fully qualified class name of the `SocialTraversalSourceDsl`: + +[source,java] +---- +@GremlinDsl(traversalSource = "com.company.SocialTraversalSourceDsl") +public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S, E> { + ... +} +---- + +It is then possible to use the `persons()` method to start traversals: + +[source,java] +---- +SocialTraversalSource social = graph.traversal(SocialTraversalSource.class); +social.persons().count(); +---- + +NOTE: Using Maven, as shown in the `gremlin-archetype-dsl` module, makes developing DSLs with the annotation processor +straightforward in that it sets up appropriate paths to the generated code automatically. + +Gremlin-Python +~~~~~~~~~~~~~~
