| //// |
| 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. |
| //// |
| [[gremlin-variants]] |
| = Gremlin Variants |
| |
| image::gremlin-house-of-mirrors.png[width=1024] |
| |
| Gremlin is a graph traversal language that makes use of two fundamental programming constructs: |
| link:https://en.wikipedia.org/wiki/Function_composition[function composition] and |
| link:https://en.wikipedia.org/wiki/Nested_function[function nesting]. Given this generality, it is possible to embed |
| Gremlin in any modern programming language. |
| |
| IMPORTANT: Gremlin-Java is the canonical representation of Gremlin and any (proper) Gremlin language variant will emulate its |
| structure as best as possible given the constructs of the host language. A strong correspondence between variants ensures |
| that the general Gremlin reference documentation is applicable to all variants and that users moving between development |
| languages can easily adopt the Gremlin variant for that language. |
| |
| image::gremlin-variant-architecture.png[width=650,float=left] |
| |
| NOTE: The information herein describes how to use the Gremlin language variants distributed |
| with Apache TinkerPop. For information on how to build a Gremlin language variant, |
| please review the link:https://tinkerpop.apache.org/docs/current/tutorials/gremlin-language-variants/[Gremlin Language Variants] |
| tutorial. |
| |
| [[gremlin-java]] |
| == Gremlin-Java |
| |
| image:gremlin-java-drawing.png[width=130,float=right] Apache TinkerPop's Gremlin-Java implements Gremlin within the Java8 |
| language and can be used by any Java8 compliant virtual machine. Gremlin-Java is considered the canonical, reference |
| implementation of Gremlin and serves as the foundation by which all other Gremlin language variants should emulate. |
| |
| === The Lambda Solution |
| |
| Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as |
| most languages do not support lambda introspection and thus, code analysis. In Gremlin-Java, Java8 lambdas can be leveraged. |
| |
| [source,java] |
| g.V().out("knows").map(t -> t.get().value("name") + " is the friend name") <1> |
| g.V().out("knows").sideEffect(System.out::println) <2> |
| g.V().as("a").out("knows").as("b").select("b").by((Function<Vertex, Integer>) v -> v.<String>value("name").length()) <3> |
| |
| <1> A Java8 function is used to map a `Traverser<S>` to an object `E`. |
| <2> Gremlin steps that take consumer arguments can be passed Java8 method references. |
| <3> Gremlin-Java may sometimes require explicit lambda typing when types can not be automatically inferred. |
| |
| When sending traversals over the wire via a `RemoteConnection`, the static methods of `Lambda` should be used |
| and should denote a particular JSR-223 `ScriptEngine`. `Lambda` creates a string-based lambda that is then converted |
| into a lambda/closure/anonymous-function/etc. by the respective lambda language's JSR-223 `ScriptEngine` implementation. |
| |
| [source,java] |
| g.V().out("knows").map(Lambda.function("it.get().value('name') + ' is the friend name'")) |
| g.V().out("knows").sideEffect(Lambda.consumer("println it")) |
| g.V().as("a").out("knows").as("b").select("b").by(Lambda.<Vertex,Integer>function("it.value('name').length()")) |
| |
| [[gremlin-groovy]] |
| == Gremlin-Groovy |
| |
| image:gremlin-groovy-drawing.png[width=130,float=right] Apache TinkerPop's Gremlin-Groovy implements Gremlin within the |
| link:http://groovy.apache.org[Apache Groovy] language. As a JVM-based language variant, Gremlin-Groovy is backed by |
| Gremlin-Java constructs. Moreover, given its scripting nature, Gremlin-Groovy serves as the language of |
| <<gremlin-console,Gremlin Console>>. |
| |
| WARNING: In Groovy, `as`, `in`, and `not` are reserved words. Gremlin-Groovy does not allow these steps to be called |
| statically from the anonymous traversal `+__+` and therefore, must always be prefixed with `+__+`. For instance: |
| `+g.V().as('a').in().as('b').where(__.not(__.as('a').out().as('b')))+` |
| |
| [[gremlin-python]] |
| == Gremlin-Python |
| |
| image:gremlin-python-drawing.png[width=130,float=right] Apache TinkerPop's Gremlin-Python implements Gremlin within |
| the link:https://www.python.org/[Python] language and can be used on any Python virtual machine including the popular |
| link:https://en.wikipedia.org/wiki/CPython[CPython] machine. Python's syntax has the same constructs as Java including |
| "dot notation" for function chaining (`a.b.c`), round bracket function arguments (`a(b,c)`), and support for global |
| namespaces (`a(b())` vs `a(__.b())`). As such, anyone familiar with Gremlin-Java will immediately be able to work |
| with Gremlin-Python. Moreover, there are a few added constructs to Gremlin-Python that make traversals a bit more succinct. |
| |
| WARNING: In Python, `and`, `as`, `from`, `global`, `in`, `is`, `not`, and `or` are reserved words. Gremlin-Python simply |
| postfixes `+_+` to the end of these terms for their use with graph traversal. For instance: `g.V().as_('a').in_().as_('b').select('a','b')`. |
| |
| To install Gremlin-Python, use Python's link:https://en.wikipedia.org/wiki/Pip_(package_manager)[pip] package manager. |
| |
| [source,bash] |
| pip install gremlinpython |
| |
| Gremlin-Python users will typically make use of the following classes. |
| |
| [source,python] |
| >>> from gremlin_python import statics |
| >>> from gremlin_python.process.anonymous_traversal import traversal |
| >>> from gremlin_python.process.graph_traversal import __ |
| >>> from gremlin_python.process.strategies import * |
| >>> from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection |
| |
| In Gremlin-Python there exists `GraphTraversalSource`, `GraphTraversal`, and `__` which mirror the respective classes |
| in Gremlin-Java. The `GraphTraversalSource` requires a driver in order to communicate with |
| <<gremlin-server,GremlinServer>> (or any <<connecting-via-remotegraph,`RemoteConnection`>>-enabled server). The |
| `gremlin_python.driver.driver_remote_connection` is provided with Apache TinkerPop's Gremlin-Python distribution. |
| |
| IMPORTANT: For developers wishing to provide another *driver implementation*, be sure to extend `RemoteConnection` in |
| `gremlin_python.driver` so it can then be used by Gremlin-Python's `GraphTraversal`. |
| |
| When Gremlin Server is running, Gremlin-Python can communicate with Gremlin Server. The `conf/gremlin-server-modern-py.yaml` |
| configuration maintains a `GremlinJythonScriptEngine` as well as the appropriate serializers for communicating `Bytecode`. |
| |
| IMPORTANT: Gremlin-Python is not compatible with GraphSON 1.0. |
| |
| [source,bash] |
| ---- |
| $ bin/gremlin-server.sh install org.apache.tinkerpop gremlin-python x.y.z |
| $ bin/gremlin-server.sh conf/gremlin-server-modern-py.yaml |
| [INFO] GremlinServer - |
| \,,,/ |
| (o o) |
| ---oOOo-(3)-oOOo--- |
| |
| [INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-modern-py.yaml |
| [INFO] MetricManager - Configured Metrics Slf4jReporter configured with interval=180000ms and loggerName=org.apache.tinkerpop.gremlin.server.Settings$Slf4jReporterMetrics |
| [INFO] GraphManager - Graph [graph] was successfully configured via [conf/tinkergraph-empty.properties]. |
| [INFO] ServerGremlinExecutor - Initialized Gremlin thread pool. Threads in pool named with pattern gremlin-* |
| [INFO] Logger - 51 attributes loaded from 40 stream(s) in 16ms, 51 saved, 614 ignored: ["Ant-Version", "Archiver-Version", "Bnd-LastModified", "Boot-Class-Path", "Build-Jdk", "Build-Version", "Built-By", "Bundle-ClassPath", "Bundle-Description", "Bundle-DocURL", "Bundle-License", "Bundle-ManifestVersion", "Bundle-Name", "Bundle-RequiredExecutionEnvironment", "Bundle-SymbolicName", "Bundle-Vendor", "Bundle-Version", "Can-Redefine-Classes", "Created-By", "DynamicImport-Package", "Eclipse-BuddyPolicy", "Export-Package", "Extension-Name", "Extension-name", "Fragment-Host", "Ignore-Package", "Implementation-Build", "Implementation-Title", "Implementation-URL", "Implementation-Vendor", "Implementation-Vendor-Id", "Implementation-Version", "Import-Package", "Include-Resource", "JCabi-Build", "JCabi-Date", "JCabi-Version", "Main-Class", "Main-class", "Manifest-Version", "Originally-Created-By", "Private-Package", "Require-Capability", "Specification-Title", "Specification-Vendor", "Specification-Version", "Tool", "X-Compile-Source-JDK", "X-Compile-Target-JDK", "hash", "version"] |
| [INFO] ScriptEngines - Loaded gremlin-jython ScriptEngine |
| [INFO] ScriptEngines - Loaded gremlin-python ScriptEngine |
| [INFO] ScriptEngines - Loaded gremlin-groovy ScriptEngine |
| [INFO] GremlinExecutor - Initialized gremlin-groovy ScriptEngine with scripts/generate-modern.groovy |
| [INFO] ServerGremlinExecutor - Initialized GremlinExecutor and configured ScriptEngines. |
| [INFO] ServerGremlinExecutor - A GraphTraversalSource is now bound to [g] with graphtraversalsource[tinkergraph[vertices:0 edges:0], standard] |
| [INFO] OpLoader - Adding the standard OpProcessor. |
| [INFO] OpLoader - Adding the session OpProcessor. |
| [INFO] OpLoader - Adding the traversal OpProcessor. |
| [INFO] TraversalOpProcessor - Initialized cache for TraversalOpProcessor with size 1000 and expiration time of 600000 ms |
| [INFO] GremlinServer - Executing start up LifeCycleHook |
| [INFO] Logger$info - Loading 'modern' graph data. |
| [INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+gryo with org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0 |
| [INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+gryo-stringd with org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0 |
| [INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0 |
| [INFO] AbstractChannelizer - Configured application/json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0 |
| [INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 4 and boss thread pool of 1. |
| [INFO] GremlinServer$1 - Channel started at port 8182. |
| ---- |
| |
| NOTE: The command to use `install` need only be executed once to gather `gremlin-python` dependencies into Gremlin Servers' |
| path. Future starts of Gremlin Server will not require that command. |
| |
| Within CPython console, a `GraphTraversalSource` is created from the anonymous `traversal()` method where the "g" |
| provided to the `DriverRemoteConnection` corresponds to the name of a `GraphTraversalSource` on the remote end. |
| |
| [source,python] |
| >>> g = traversal().withRemote(DriverRemoteConnection('ws://localhost:8182/gremlin','g')) |
| |
| When a traversal from the `GraphTraversalSource` is iterated, the traversal's `Bytecode` is sent over the wire |
| via the registered `RemoteConnection`. The bytecode is used to construct the equivalent traversal at the remote |
| traversal source. Moreover, typically the bytecode is analyzed to determine which language the bytecode should be |
| translated to. If the traversal does not contain lambdas, the remote location (e.g. Gremlin Server) will typically |
| use Gremlin-Java. If it has lambdas written in Groovy, it will use Gremlin-Groovy (e.g. `GremlinGroovyScriptEngine`). |
| Likewise, if it has lambdas represented in Python, it will use Gremlin-Python (e.g. `GremlinJythonScriptEngine`). |
| |
| IMPORTANT: Gremlin-Python's `Traversal` class supports the standard Gremlin methods such as `next()`, `nextTraverser()`, |
| `toSet()`, `toList()`, etc. Such "terminal" methods trigger the evaluation of the traversal. |
| |
| === RemoteConnection Submission |
| |
| There are various ways to submit a traversal to a `RemoteConnection`. Just as in Gremlin-Java, there are various |
| "terminal/action methods" off of `Traversal`. |
| |
| * `Traversal.next()` |
| * `Traversal.nextTraverser()` |
| * `Traversal.toList()` |
| * `Traversal.toSet()` |
| * `Traversal.iterate()` |
| |
| If you need to send additional headers in the websockets connection, you can pass an optional `headers` parameter |
| to the `DriverRemoteConnection` constructor. |
| |
| [source,python] |
| g = traversal().withRemote(DriverRemoteConnection('ws://localhost:8182/gremlin','g',headers={'Header':'Value'})) |
| |
| === Gremlin-Python Sugar |
| |
| Python supports meta-programming and operator overloading. There are three uses of these techniques in Gremlin-Python that |
| makes traversals a bit more concise. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V().both()[1:3].toList() |
| g.V().both()[1].toList() |
| g.V().both().name.toList() |
| ---- |
| |
| === Static Enums and Methods |
| |
| Gremlin has various tokens (e.g. `T`, `P`, `Order`, `Operator`, etc.) that are represented in Gremlin-Python as Python `Enums`. |
| |
| [source,python] |
| >>> from gremlin_python.process.traversal import T |
| >>> from gremlin_python.process.traversal import Order |
| >>> from gremlin_python.process.traversal import Cardinality |
| >>> from gremlin_python.process.traversal import Column |
| >>> from gremlin_python.process.traversal import Direction |
| >>> from gremlin_python.process.traversal import Operator |
| >>> from gremlin_python.process.traversal import P |
| >>> from gremlin_python.process.traversal import Pop |
| >>> from gremlin_python.process.traversal import Scope |
| >>> from gremlin_python.process.traversal import Barrier |
| |
| These can be used analogously to how they are used in Gremlin-Java. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V().hasLabel('person').has('age',P.gt(30)).order().by('age',Order.desc).toList() |
| ---- |
| |
| Moreover, by importing the `statics` of Gremlin-Python, the class prefixes can be omitted. |
| |
| [source,python] |
| >>> statics.load_statics(globals()) |
| |
| With statics loaded its possible to represent the above traversal as below. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V().hasLabel('person').has('age',gt(30)).order().by('age',desc).toList() |
| ---- |
| |
| Finally, statics includes all the `+__+`-methods and thus, anonymous traversals like `+__.out()+` can be expressed as |
| below. That is, without the `+__+`-prefix. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V().repeat(out()).times(2).name.fold().toList() |
| ---- |
| |
| === Bindings |
| |
| When a traversal bytecode is sent over a `RemoteConnection` (e.g. Gremlin Server), it will be translated, compiled, |
| and then executed. If the same traversal is sent again, translation and compilation can be skipped as the previously |
| compiled version should be cached. Many traversals are unique up to some parameterization. For instance, |
| `g.V(1).out('created').name` is considered different from `g.V(4).out('created').name'` as they have different script |
| "string" representations. However, `g.V(x).out('created').name` with bindings of `{x : 1}` and `{x : 4}` are considered |
| the same. If a traversal is going to be executed repeatedly, but with different parameters, then bindings should be |
| used. In Gremlin-Python, bindings are 2-tuples and used as follows. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V(('id',1)).out('created').name.toList() |
| g.V(('id',4)).out('created').name.toList() |
| ---- |
| |
| === Traversal Strategies |
| |
| In order to add and remove <<traversalstrategy,traversal strategies>> from a traversal source, Gremlin-Python has a |
| `TraversalStrategy` class along with a collection of subclasses that mirror the standard Gremlin-Java strategies. |
| |
| [gremlin-python,modern] |
| ---- |
| g = g.withStrategies(SubgraphStrategy(vertices=hasLabel('person'),edges=has('weight',gt(0.5)))) |
| g.V().name.toList() |
| g.V().outE().valueMap(True).toList() |
| g = g.withoutStrategies(SubgraphStrategy) |
| g.V().name.toList() |
| g.V().outE().valueMap(True).toList() |
| g = g.withComputer(workers=2,vertices=has('name','marko')) |
| g.V().name.toList() |
| g.V().outE().valueMap(True).toList() |
| ---- |
| |
| NOTE: Many of the `TraversalStrategy` classes in Gremlin-Python are proxies to the respective strategy on |
| Apache TinkerPop's JVM-based Gremlin traversal machine. As such, their `apply(Traversal)` method does nothing. However, |
| the strategy is encoded in the Gremlin-Python bytecode and transmitted to the Gremlin traversal machine for |
| re-construction machine-side. |
| |
| === The Lambda Solution |
| |
| Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as |
| most languages do not support lambda introspection and thus, code analysis. In Gremlin-Python, |
| a link:https://docs.python.org/2/reference/expressions.html#lambda[Python lambda] should be represented as a zero-arg |
| callable that returns a string representation of a lambda. The default lambda language is `gremlin-python` and can be |
| changed via `gremlin_python.statics.default_lambda_language`. When the lambda is represented in `Bytecode` its language |
| is encoded such that the remote connection host can infer which translator and ultimate execution engine to use. |
| |
| [gremlin-python,modern] |
| ---- |
| g.V().out().map(lambda: "lambda x: len(x.get().value('name'))").sum().toList() <1> |
| statics.default_lambda_language <2> |
| g.V().out().map(lambda: ("it.get().value('name').length()", "gremlin-groovy")).sum().toList() <3> |
| statics.default_lambda_language = 'gremlin-groovy' <4> |
| g.V().out().map(lambda: "it.get().value('name').length()").sum().toList() <5> |
| g.V().out().map(lambda: ("lambda x: len(x.get().value('name'))", "gremlin-python")).sum().toList() <6> |
| statics.default_lambda_language = 'gremlin-python' <7> |
| g.V().out().map(lambda: "x: len(x.get().value('name'))").sum().toList() <8> |
| ---- |
| |
| <1> A zero-arg lambda yields a string representation of a lambda in Gremlin-Python. |
| <2> The default lambda language is currently Gremlin-Python. |
| <3> A zero-arg lambda yields a 2-tuple where the second element is the language of the lambda (Gremlin-Groovy). |
| <4> The default lambda language can be statically changed. |
| <5> A zero-arg lambda yields a string representation of a closure in Gremlin-Groovy. |
| <6> A zero-arg lambda yields a 2-tuple where the second element is the language of the lambda (Gremlin-Python). |
| <7> The default lambda language is changed back to Gremlin-Python. |
| <8> If the `lambda`-prefix is not provided, then it is appended automatically in order to give a more natural look to the expression. |
| |
| TIP: When running into situations where Groovy cannot properly discern a method signature based on the `Lambda` |
| instance created, it will help to fully define the closure in the lambda expression - so rather than |
| `lambda: ("it.get().value("name')","gremlin-groovy")`, prefer `lambda: ("x -> x.get().value("name"),"gremlin-groovy")`. |
| |
| WARNING: Jython support has been deprecated as for 3.3.10 and will be removed in 3.5.0. Gremlin-Python will at that |
| point default to Groovy for lambda processing and Python lambdas will not be supported. |
| |
| === Limitations |
| |
| * Traversals that return a `Set` *might* be coerced to a `List` in Python. In the case of Python, number equality |
| is different from JVM languages which produces different `Set` results when those types are in use. When this case |
| is detected during deserialization, the `Set` is coerced to a `List` so that traversals return consistent |
| results within a collection across different languages. If a `Set` is needed then convert `List` results |
| to `Set` manually. |
| |
| === Submit Gremlin Scripts |
| |
| Additionally, you can also send parametrized Gremlin scripts to the server as strings: |
| |
| [source,python] |
| ---- |
| client = Client('ws://localhost:8182/gremlin', 'g') |
| client.submit('x + x', {'x': 2}).all().result() |
| ---- |
| |
| It is also possible to initialize the `Client` to use <<sessions,sessions>>: |
| |
| [source,python] |
| ---- |
| client = Client('ws://localhost:8182/gremlin', 'g', session=str(uuid.uuid4())) |
| ---- |
| |
| With this configuration, the state of variables within scripts are preserved between requests. |
| |
| [[gremlin-DotNet]] |
| == Gremlin.Net |
| |
| image:gremlin-dotnet-logo.png[width=371,float=right] Apache TinkerPop's Gremlin.Net implements Gremlin within the C# |
| language. It targets .NET Standard and can therefore be used on different operating systems and with different .NET |
| frameworks, such as .NET Framework and link:https://www.microsoft.com/net/core[.NET Core]. Since the C# syntax is very |
| similar to that of Java, it should be very easy to switch between Gremlin-Java and Gremlin.Net. The only major |
| syntactical difference is that all method names in Gremlin.Net use PascalCase as opposed to camelCase in Gremlin-Java |
| in order to comply with .NET conventions. |
| |
| [source,powershell] |
| nuget install Gremlin.Net |
| |
| In Gremlin.Net there exists `GraphTraversalSource`, `GraphTraversal`, and `__` which mirror the respective classes |
| in Gremlin-Java. The `GraphTraversalSource` requires a driver in order to communicate with <<gremlin-server,GremlinServer>> (or any |
| RemoteConnection-enabled server). |
| |
| The `Gremlin.Net.Driver.Remote.DriverRemoteConnection` is provided as part of Apache TinkerPop’s Gremlin.Net. |
| |
| IMPORTANT: For developers wishing to provide another driver implementation, be sure to implement `IRemoteConnection` in |
| `Gremlin.Net.Process.Remote` so it can then be used by Gremlin.Net’s `GraphTraversal`. |
| |
| When Gremlin Server is running, Gremlin.Net can communicate with Gremlin Server by sending traversals serialized as `Bytecode`. |
| |
| IMPORTANT: Gremlin.Net is not compatible with GraphSON 1.0. |
| |
| A traversal source can be spawned anonymously using an `AnonymousTraversalSource` which can be statically imported as |
| follows: |
| |
| [source,csharp] |
| ---- |
| using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource; |
| ---- |
| |
| which will expose a static `Traversal()` method which can be used as follows to yield a `GraphTraversalSource` assigned |
| to "g" and configured to connect to Gremlin Server at "localhost:8182": |
| |
| [source,csharp] |
| ---- |
| var remoteConnection = new DriverRemoteConnection(new GremlinClient(new GremlinServer("localhost", 8182))); |
| var g = Traversal().WithRemote(remoteConnection); |
| ---- |
| |
| When a traversal from the `GraphTraversalSource` is iterated, the traversal’s `Bytecode` is sent over the wire via the registered |
| `IRemoteConnection`. The bytecode is used to construct the equivalent traversal at the remote traversal source. |
| Moreover, typically the bytecode is analyzed to determine which language the bytecode should be translated to. If the traversal |
| does not contain lambdas, the remote location (e.g. Gremlin Server) will typically |
| use Gremlin-Java. If it has lambdas written in Groovy, it will use Gremlin-Groovy (e.g. `GremlinGroovyScriptEngine`). |
| Likewise, if it has lambdas represented in Python, it will use Gremlin-Python (e.g. `GremlinJythonScriptEngine`). |
| |
| IMPORTANT: Gremlin.Net’s `ITraversal` interface supports the standard Gremlin methods such as `Next()`, `NextTraverser()`, `ToSet()`, |
| `ToList()`, etc. Such "terminal" methods trigger the evaluation of the traversal. |
| |
| === RemoteConnection Submission |
| |
| Very similar to Gremlin-Python and Gremlin-Java, there are various ways to submit a traversal to a `IRemoteConnection` using |
| terminal/action methods off of `ITraversal`. |
| |
| * `ITraversal.Next()` |
| * `ITraversal.NextTraverser()` |
| * `ITraversal.ToList()` |
| * `ITraversal.ToSet()` |
| * `ITraversal.Iterate()` |
| |
| === Configuration |
| |
| The following sections describe how the Gremlin.Net driver can be configured. |
| |
| ==== Gremlin Server |
| |
| The connection properties for the Gremlin.Net driver can be passed to the `GremlinServer` instance as keyword arguments: |
| |
| [width="100%",cols="3,10,^2",options="header"] |
| |========================================================= |
| |Key |Description |Default |
| |hostname |The hostname that the driver will connect to. |localhost |
| |port |The port on which Gremlin Server can be reached. |8182 |
| |enableSsl |Determines if SSL should be enabled or not. If enabled on the server then it must be enabled on the client. |false |
| |username |The username to submit on requests that require authentication. |_none_ |
| |password |The password to submit on requests that require authentication. |_none_ |
| |========================================================= |
| |
| ==== GraphSON Serialization |
| |
| The Gremlin.Net driver uses by default GraphSON 3.0 but it is also possible to use GraphSON 2.0 which can be necessary |
| when the server does not support GraphSON 3.0 yet: |
| |
| [source,csharp] |
| ---- |
| var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2Reader(), |
| new GraphSON2Writer(), GremlinClient.GraphSON2MimeType); |
| ---- |
| |
| === Static Enums and Methods |
| |
| Gremlin has various tokens (e.g. `T`, `P`, `Order`, `Operator`, etc.) that are represented in Gremlin.Net as classes. |
| |
| These can be used analogously to how they are used in Gremlin-Java. |
| |
| [source,csharp] |
| g.V().HasLabel("person").Has("age",P.Gt(30)).Order().By("age",Order.desc).ToList() |
| |
| Moreover, the class prefixes can be omitted with a `using static`. |
| |
| [source,csharp] |
| ---- |
| using static Gremlin.Net.Process.Traversal.P; |
| using static Gremlin.Net.Process.Traversal.Order; |
| ---- |
| |
| Then it is possible to represent the above traversal as below. |
| |
| [source,csharp] |
| g.V().HasLabel("person").Has("age",Gt(30)).Order().By("age",desc).ToList() |
| |
| Finally, with using static `+__+`, anonymous traversals like `+__.Out()+` can be expressed as below. That is, without the ``+__.+``-prefix. |
| |
| [source,csharp] |
| g.V().Repeat(Out()).Times(2).Values("name").Fold().ToList() |
| |
| === Bindings |
| |
| When a traversal bytecode is sent over a `IRemoteConnection` (e.g. Gremlin Server), it will be translated, compiled, |
| and then executed. If the same traversal is sent again, translation and compilation can be skipped as the previously |
| compiled version should be cached. Many traversals are unique up to some parameterization. For instance, |
| `g.V(1).Out("created").Values("name")` is considered different from `g.V(4).Out("created").Values("Name")` |
| as they have different script "string" representations. However, `g.V(x).Out("created").Values("name")` with bindings of |
| `{x : 1}` and `{x : 4}` are considered the same. If a traversal is going to be executed repeatedly, but with different |
| parameters, then bindings should be used. In Gremlin.Net, bindings are objects that can be created as follows. |
| |
| [source,csharp] |
| ---- |
| var b = new Bindings(); |
| g.V(b.Of("id", 1)).Out("created").Values("name").toList() |
| g.V(b.Of("id", 4)).Out("created").Values("name").toList() |
| ---- |
| |
| === Traversal Strategies |
| |
| In order to add and remove traversal strategies from a traversal source, Gremlin.Net has an `AbstractTraversalStrategy` |
| class along with a collection of subclasses that mirror the standard Gremlin-Java strategies. |
| |
| [source,csharp] |
| ---- |
| g = g.WithStrategies(new SubgraphStrategy(vertexCriterion: HasLabel("person"), |
| edgeCriterion: Has("weight", Gt(0.5)))); |
| var names = g.V().Values("name").ToList(); // names: [marko, vadas, josh, peter] |
| |
| g = g.WithoutStrategies(typeof(SubgraphStrategy)); |
| names = g.V().Values("name").ToList(); // names: [marko, vadas, lop, josh, ripple, peter] |
| |
| var edgeValueMaps = g.V().OutE().ValueMap(true).ToList(); |
| // edgeValueMaps: [[label:created, id:9, weight:0.4], [label:knows, id:7, weight:0.5], [label:knows, id:8, weight:1.0], |
| // [label:created, id:10, weight:1.0], [label:created, id:11, weight:0.4], [label:created, id:12, weight:0.2]] |
| |
| g = g.WithComputer(workers: 2, vertices: Has("name", "marko")); |
| names = g.V().Values("name").ToList(); // names: [marko] |
| |
| edgeValueMaps = g.V().OutE().ValueMap(true).ToList(); |
| // edgeValueMaps: [[label:created, id:9, weight:0.4], [label:knows, id:7, weight:0.5], [label:knows, id:8, weight:1.0]] |
| ---- |
| |
| NOTE: Many of the TraversalStrategy classes in Gremlin.Net are proxies to the respective strategy on Apache TinkerPop’s |
| JVM-based Gremlin traversal machine. As such, their `Apply(ITraversal)` method does nothing. However, the strategy is |
| encoded in the Gremlin.Net bytecode and transmitted to the Gremlin traversal machine for re-construction machine-side. |
| |
| === The Lambda Solution |
| |
| Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as |
| most languages do not support lambda introspection and thus, code analysis. While Gremlin.Net doesn't support C# lambdas, it |
| is still able to represent lambdas in other languages. When the lambda is represented in `Bytecode` its language is encoded |
| such that the remote connection host can infer which translator and ultimate execution engine to use. |
| |
| [source,csharp] |
| ---- |
| g.V().Out().Map<int>(Lambda.Groovy("it.get().value('name').length()")).Sum<int>().ToList(); <1> |
| g.V().Out().Map<int>(Lambda.Python("lambda x: len(x.get().value('name'))")).Sum<int>().ToList(); <2> |
| ---- |
| |
| <1> `Lambda.Groovy()` can be used to create a Groovy lambda. |
| <2> `Lambda.Python()` can be used to create a Python lambda. |
| |
| The `ILambda` interface returned by these two methods inherits interfaces like `IFunction` and `IPredicate` that mirror |
| their Java counterparts which makes it possible to use lambdas with Gremlin.Net for the same steps as in Gremlin-Java. |
| |
| TIP: When running into situations where Groovy cannot properly discern a method signature based on the `Lambda` |
| instance created, it will help to fully define the closure in the lambda expression - so rather than |
| `Lambda.Groovy("it.get().value('name'))`, prefer `Lambda.Groovy("x -> x.get().value('name'))`. |
| |
| === Submit Gremlin Scripts |
| |
| Additionally, you can also send parametrized Gremlin scripts to the server as strings: |
| |
| [source,csharp] |
| ---- |
| var gremlinServer = new GremlinServer("localhost", 8182); |
| using (var gremlinClient = new GremlinClient(gremlinServer)) |
| { |
| var bindings = new Dictionary<string, object> {{"a", a}, {"b", b}}; |
| var response = |
| await gremlinClient.SubmitWithSingleResultAsync<int>("a + b", bindings); |
| } |
| ---- |
| |
| It is also possible to initialize the `Client` to use <<sessions,sessions>>: |
| |
| [source,csharp] |
| ---- |
| var gremlinServer = new GremlinServer("localhost", 8182); |
| var client = new GremlinClient(gremlinServer, sessionId: Guid.NewGuid().ToString())) |
| ---- |
| |
| With this configuration, the state of variables within scripts are preserved between requests. |
| |
| [[gremlin-javascript]] |
| == Gremlin-JavaScript |
| |
| image:gremlin-js.png[width=130,float=right] Apache TinkerPop's Gremlin-JavaScript implements Gremlin within the |
| JavaScript language. It targets Node.js runtime and can be used on different operating systems on any Node.js 6 or |
| above. Since the JavaScript naming conventions are very similar to that of Java, it should be very easy to switch |
| between Gremlin-Java and Gremlin-JavaScript. |
| |
| [source,bash] |
| npm install gremlin |
| |
| The Gremlin-JavaScript provides `GraphTraversalSource`, `GraphTraversal`, and `__` which mirror the respective classes |
| in Gremlin-Java. The `GraphTraversalSource` requires a RemoteConnection implementation in order to communicate with |
| <<gremlin-server,GremlinServer>>. |
| |
| [source,javascript] |
| ---- |
| const gremlin = require('gremlin'); |
| const traversal = gremlin.process.AnonymousTraversalSource.traversal; |
| const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection; |
| ---- |
| |
| A traversal source can be spawned with `RemoteStrategy` from an `AnonymousTraversalSource`. |
| |
| [source,javascript] |
| ---- |
| const g = traversal().withRemote(new DriverRemoteConnection('ws://localhost:8182/gremlin')); |
| ---- |
| |
| Gremlin-JavaScript supports plain text SASL authentication, you can set it on the connection options. |
| |
| [source,javascript] |
| ---- |
| const authenticator = new gremlin.driver.auth.PlainTextSaslAuthenticator('myuser', 'mypassword'); |
| const g = traversal().withRemote(new DriverRemoteConnection('ws://localhost:8182/gremlin', { authenticator }); |
| ---- |
| |
| When a traversal from the `GraphTraversalSource` is iterated, the traversal’s `Bytecode` is sent over the wire via |
| the registered `RemoteConnection`. The bytecode is used to construct the equivalent traversal at the remote |
| traversal source. |
| |
| Since Gremlin-JavaScript currently doesn't support lambda expressions, all traversals can be translated to |
| Gremlin-Java on the remote location (e.g. Gremlin Server). |
| |
| WARNING: In Javascript, `from` and `in` are reserved words. Gremlin-Javascript simply postfixes `+_+` to the end of |
| these terms for their use with graph traversal. For instance: `g.V().in_().out()` |
| |
| IMPORTANT: Gremlin-JavaScript’s `Traversal` base class supports the standard Gremlin methods such as `next()` and |
| `toList()` Such "terminal" methods trigger the evaluation of the traversal. |
| |
| === RemoteConnection Submission |
| |
| In a similar way as in other GLVs, there are various ways to submit a traversal to a |
| `RemoteConnection` using terminal/action methods off of `Traversal`. Given that I/O operations in Node.js are |
| asynchronous by default, this terminal methods return a `Promise`. |
| |
| * `Traversal.toList()`: Returns a `Promise` with an `Array` as result value. |
| * `Traversal.next()`: Returns a `Promise` with a `{ value, done }` tuple as result value, according to the |
| link:https://github.com/tc39/proposal-async-iteration[async iterator proposal]. |
| * `Traversal.iterate()`: Returns a `Promise` without a value. |
| |
| For example: |
| |
| [source,javascript] |
| ---- |
| g.V().hasLabel('person').values('name').toList() |
| .then(names => console.log(names)); |
| ---- |
| |
| You can `await` the promises if you are using `async` functions. |
| |
| [source,javascript] |
| ---- |
| const names = await g.V().hasLabel('person').values('name').toList(); |
| console.log(names); |
| ---- |
| |
| === Static Enums and Methods |
| |
| Gremlin has various tokens (e.g. `t`, `P`, `order`, `direction`, etc.) that are represented in Gremlin-JavaScript as |
| objects. |
| |
| [source,javascript] |
| g.V().hasLabel('person').has('age', P.gt(30)).order().by('age', order.desc).toList() |
| |
| These objects must be required manually from the `process` namespace: |
| |
| [source,javascript] |
| ---- |
| const gremlin = require('gremlin'); |
| const P = gremlin.process.P; |
| ---- |
| |
| Finally, using static `+__+` anonymous traversals like `+__.out()+` can be expressed as below: |
| |
| [source,javascript] |
| ---- |
| const gremlin = require('gremlin'); |
| const __ = gremlin.process.statics; |
| |
| g.V().repeat(__.out()).times(2).values("name").fold().toList(); |
| ---- |
| |
| === Submit Gremlin Scripts |
| |
| Additionally, you can also send parametrized Gremlin scripts to the server as strings, using the |
| `Client` class in Gremlin-JavaScript. |
| |
| [source,javascript] |
| ---- |
| const gremlin = require('gremlin'); |
| const client = new gremlin.driver.Client('ws://localhost:8182/gremlin', { traversalSource: 'g' }); |
| |
| const result1 = await client.submit('g.V(vid)', { vid: 1 }); |
| const vertex = result1.first(); |
| |
| const result2 = await client.submit('g.V().hasLabel(label).tail(n)', { label: 'person', n: 3 }); |
| |
| // ResultSet is an iterable |
| for (const vertex of result2) { |
| console.log(vertex.id); |
| } |
| ---- |
| |
| It is also possible to initialize the `Client` to use <<sessions,sessions>>: |
| |
| [source,javascript] |
| ---- |
| const client = new gremlin.driver.Client('ws://localhost:8182/gremlin', { traversalSource: 'g', 'session': 'unique-string-id' }); |
| ---- |
| |
| With this configuration, the state of variables within scripts are preserved between requests. |
| |