blob: 69988bd5d4ad4d5e665b6d94b9d678918d8bb980 [file] [log] [blame]
////
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.