From 9eb2e0e95d2624d2c15bcfa74f817dba08fdcc5f Mon Sep 17 00:00:00 2001 From: Yang Xia <55853655+xiazcy@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:22:57 -0800 Subject: [PATCH] Updates in docs to remove :remote in console, gephi plugin, bytecode instances, deprecate neo4j, etc. --- .../src/dev/developer/for-committers.asciidoc | 6 +- docs/src/recipes/anti-patterns.asciidoc | 2 +- .../reference/gremlin-applications.asciidoc | 439 ++---------------- docs/src/reference/gremlin-variants.asciidoc | 2 +- .../reference/implementations-neo4j.asciidoc | 110 ++++- .../reference/implementations-spark.asciidoc | 10 +- docs/src/reference/the-traversal.asciidoc | 13 +- gremlin-server/conf/gremlin-server.yaml | 3 +- 8 files changed, 145 insertions(+), 440 deletions(-) diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc index bb4a004458a..4a2874f79e7 100644 --- a/docs/src/dev/developer/for-committers.asciidoc +++ b/docs/src/dev/developer/for-committers.asciidoc @@ -591,10 +591,8 @@ best example of this sort of test would be one that uses the remote `Lambda` syn ** `@WithSeedStrategy` ** `@WithSubgraphStrategy` -Tag filters can be applied to Intellij at execution time by adding a system property of -`-Dcucumber.filter.tags=`. In addition, the scenarios that pass the `` can be further -filtered by name with `-Dcucumber.filter.name=`. Note that these settings will override those -configured by `CucumberOptions` on "*FeatureTest" setups. +Tag filters can be applied to Intellij at execution time by adding a system properties of +`-Dcucumber.filter.tags=`. [[gremlin-socket-server-tests]] === Gremlin Socket Server Tests diff --git a/docs/src/recipes/anti-patterns.asciidoc b/docs/src/recipes/anti-patterns.asciidoc index 839764e38ba..6fe01c7a085 100644 --- a/docs/src/recipes/anti-patterns.asciidoc +++ b/docs/src/recipes/anti-patterns.asciidoc @@ -56,7 +56,7 @@ for (relation in relations) { from("p${relation.from}"). to("p${relation.to}") } ; [] -traversalAsString = org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator.of("g").translate(t.bytecode).getScript() ; [] +traversalAsString = traversalAsString = t.gremlinLang.getGremlin() ; [] [ "Traversal String Length": traversalAsString.length() , "Traversal Preview": traversalAsString.replaceFirst(/^(.{104}).*(.{64})$/, '$1 ... $2') ] ---- diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc index 2f1d7f3d588..b67d69787e4 100644 --- a/docs/src/reference/gremlin-applications.asciidoc +++ b/docs/src/reference/gremlin-applications.asciidoc @@ -134,55 +134,16 @@ some other useful operations. The following table outlines the most commonly us |:install |:+ |Imports a Maven library and its dependencies into the Console. |:uninstall |:- |Removes a Maven library and its dependencies. A restart of the console is required for removal to fully take effect. |:plugin |:pin |Plugin management functions to list, activate and deactivate available plugins. -|:remote |:rem |Configures a "remote" context where Gremlin or results of Gremlin will be processed via usage of `:submit`. -|:submit |:> |Submit Gremlin to the currently active context defined by `:remote`. -|:bytecode |:bc |Provides options for translating and evaluating `Bytecode` for debugging purposes. |========================================================= -Many of the above commands are described elsewhere or are generally self-explanatory, but the `:bytecode` command -could use some additional explanation. The following code shows example usage: - -[source,text] ----- -gremlin> :bytecode from g.V().out('knows') <1> -==>{"@type":"g:Bytecode","@value":{"step":[["V"],["out","knows"]]}} -gremlin> :bytecode translate g {"@type":"g:Bytecode","@value":{"step":[["V"],["out","knows"]]}} <2> -==>g.V().out("knows") -gremlin> m = GraphSONMapper.build().create() -==>org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper@69d6a7cd -gremlin> :bc config m <3> -==>Configured bytecode serializer -gremlin> :bc from g.V().property('d',java.time.YearMonth.now()) <4> -Could not find a type identifier for the class : class java.time.Month. Make sure the value to serialize has a type identifier registered for its class. (through reference chain: java.time.YearMonth["month"]) -Type ':help' or ':h' for help. -Display stack trace? [yN]n -gremlin> :bc reset <5> -==>Bytecode serializer reset to GraphSON 3.0 with extensions and TinkerGraph serializers -gremlin> :bc from g.V().property('d',java.time.YearMonth.now()) -==>{"@type":"g:Bytecode","@value":{"step":[["V"],["property","d",{"@type":"gx:YearMonth","@value":"2020-11"}]]}} ----- - -<1> Generates a GraphSON 3.0 representation of the traversal as bytecode. -<2> Converts bytecode in GraphSON 3.0 format to a traversal string. -<3> Configure a custom `GraphSONMapper` for the `:bytecode` command to use which can be helpful when working with -custom classes from different graph providers. The `config` option can take a `GraphSONMapper` argument as shown or -one or more `IoRegistry` or `SimpleModule` implementations that will plug into the default `GraphSONMapper` constructed -by the `:bytecode` command. The default will configure for GraphSON 3.0 with the extensions module and, if present, -the `TinkerIoRegistry` from TinkerGraph. -<4> Note that the `YearMonth` will not serialize because `m` did not configure the extensions module. -<5> After `reset` it works properly once more. - -NOTE: The Console does expose the `:record` command which is inherited from the Groovy Shell. This command works well -with local commands, but may record session outputs differently for `:remote` commands. If there is a need to use -`:record` it may be best to manually create a `Cluster` object and issue commands that way so that they evaluate -locally in the shell. +NOTE: The Console also exposes the `:record` command which is inherited from the Groovy Shell. === Interrupting Evaluations If there is some input that is taking too long to evaluate or to iterate through, use `ctrl+c` to attempt to interrupt that process. It is an "attempt" in the sense that the long running process is only informed of the interruption by the user and must respond to it (as with any call to `interrupt()` on a `Thread`). A `Traversal` will typically respond -to such requests as do most commands, including `:remote` operations. +to such requests as do most commands. [source,text] ---- @@ -263,7 +224,6 @@ The following Gremlin Console session demonstrates the basics of these features: ---- gremlin> :plugin list <1> ==>tinkerpop.server[active] -==>tinkerpop.gephi ==>tinkerpop.utilities[active] ==>tinkerpop.sugar ==>tinkerpop.tinkergraph[active] @@ -273,7 +233,6 @@ gremlin> :install org.apache.tinkerpop neo4j-gremlin x.y.z <3> ==>loaded: [org.apache.tinkerpop, neo4j-gremlin, x.y.z] gremlin> :plugin list <4> ==>tinkerpop.server[active] -==>tinkerpop.gephi ==>tinkerpop.utilities[active] ==>tinkerpop.sugar ==>tinkerpop.tinkergraph[active] @@ -282,7 +241,6 @@ gremlin> :plugin use tinkerpop.neo4j <5> ==>tinkerpop.neo4j activated gremlin> :plugin list <6> ==>tinkerpop.server[active] -==>tinkerpop.gephi ==>tinkerpop.sugar[active] ==>tinkerpop.utilities[active] ==>tinkerpop.neo4j[active] @@ -568,7 +526,7 @@ Map params = new HashMap<>(); params.put("name","marko"); List list = client.submit("g.V().has('person','name',name).out('knows')", params).all().get(); -// bytecode +// traversal GraphTraversalSource g = traversal().with(DriverRemoteConnection.using("localhost",8182,"g")); List list = g.V().has("person","name","marko").out("knows").toList(); ---- @@ -579,7 +537,7 @@ def cluster = Cluster.open() def client = cluster.connect() def list = client.submit("g.V().has('person','name',name).out('knows')", [name: "marko"]).all().get(); -// bytecode +// traversal def g = traversal().with(DriverRemoteConnection.using("localhost",8182,"g")) def list = g.V().has('person','name','marko').out('knows').toList() ---- @@ -594,7 +552,7 @@ const client = new Client('ws://localhost:45940/gremlin', { traversalSource: "g" const conn = client.open(); const list = conn.submit("g.V().has('person','name',name).out('knows')",{name: 'marko'}).then(function (response) { ... }); -// bytecode +// traversal const g = gtraversal().with(new DriverRemoteConnection('ws://localhost:8182/gremlin')); const list = g.V().has("person","name","marko").out("knows").toList(); ---- @@ -604,7 +562,7 @@ const list = g.V().has("person","name","marko").out("knows").toList(); client = Client('ws://localhost:8182/gremlin', 'g') list = client.submit("g.V().has('person','name',name).out('knows')",{'name': 'marko'}).all() -# bytecode +# traversal g = traversal().with(DriverRemoteConnection('ws://localhost:8182/gremlin','g')) list = g.V().has("person","name","marko").out("knows").toList() ---- @@ -616,7 +574,7 @@ resultSet, err := client.SubmitWithOptions("g.V().has('person','name',name).out( new(RequestOptionsBuilder).AddBinding("name", "marko").Create()) result, err := resultSet.All() -// bytecode +// traversal remote, err := NewDriverRemoteConnection("ws://localhost:8182/gremlin") g := Traversal_().With(remote) list, err := g.V().Has("person", "name", "marko").Out("knows").ToList() @@ -636,7 +594,7 @@ and details in the <> Sec [[connecting-via-console]] === Connecting via Console -With Gremlin Server running it is now possible to issue some scripts to it for processing. Start Gremlin Console as +Gremlin Console can be used to send remote traversals to a running Gremlin Server. Start Gremlin Console as follows: [source,text] @@ -649,177 +607,32 @@ $ bin/gremlin.sh gremlin> ---- -The console has the notion of a "remote", which represents a place a script will be sent from the console to be -evaluated elsewhere in some other context (e.g. Gremlin Server, Hadoop, etc.). To create a remote in the console, -do the following: - -[gremlin-groovy] ----- -:remote connect tinkerpop.server conf/remote.yaml ----- - -The `:remote` command shown above displays the current status of the remote connection. This command can also be -used to configure a new connection and change other related settings. To actually send a script to the server a -different command is required: +Remote traversals can then be constructed as follows. [gremlin-groovy] ---- -:> g.V().values('name') -:> g.V().has('name','marko').out('created').values('name') -:> g.E().label().groupCount() -result -:remote close ----- - -The `:>` command, which is a shorthand for `:submit`, sends the script to the server to execute there. Results are -wrapped in an `Result` object which is a just a holder for each individual result. The `class` shows the data type -for the containing value. Note that the last script sent was supposed to return a `Map`, but its `class` is -`java.lang.String`. By default, the connection is configured to only return text results. In other words, -Gremlin Server is using `toString` to serialize all results back to the console. This enables virtually any -object on the server to be returned to the console, but it doesn't allow the opportunity to work with this data -in any way in the console itself. A different configuration of the `:remote` is required to get the results back -as "objects": - -[gremlin-groovy] ----- -:remote connect tinkerpop.server conf/remote-objects.yaml <1> -:remote list <2> -:> g.E().label().groupCount() <3> -m = result[0].object <4> -m.sort {it.value} -script = """ - g.V().hasLabel('person'). - out('knows'). - out('created'). - group(). - by('name') - """ -:> @script <5> -:remote close ----- - -<1> This configuration file specifies that results should be deserialized back into an `Object` in the console with -the caveat being that the server and console both know how to serialize and deserialize the result to be returned. -<2> There are now two configured remote connections. The one marked by an asterisk is the one that was just created -and denotes the current one that `:submit` will react to. -<3> When the script is executed again, the `class` is no longer shown to be a `java.lang.String`. It is instead a `java.util.HashMap`. -<4> The last result of a remote script is always stored in the reserved variable `result`, which allows access to -the `Result` and by virtue of that, the `Map` itself. -<5> If the submission requires multiple-lines to express, then a multi-line string can be created. The `:>` command -realizes that the user is referencing a variable via `@` and submits the string script. - -TIP: In Groovy, `""" text """` is a convenient way to create a multi-line string and works well in concert with -`:> @variable`. Note that this model of submitting a string variable works for all `:>` based plugins, not just Gremlin Server. - -WARNING: Not all values that can be returned from a Gremlin script end up being serializable. For example, -submitting `:> graph` will return a `Graph` instance and in most cases those are not serializable by Gremlin Server -and will return a serialization error. It should be noted that `TinkerGraph`, as a convenience for shipping around -small sub-graphs, is serializable from Gremlin Server. - -The alternative syntax to connecting allows for the `Cluster` to be user constructed directly in the console as -opposed to simply providing a static YAML file. - -[gremlin-groovy] ----- -cluster = Cluster.open() -:remote connect tinkerpop.server cluster ----- - -The Gremlin Server `:remote config` command for the driver has the following configuration options: - -[width="100%",cols="3,10a",options="header"] -|========================================================= -|Command |Description -|alias | -[width="100%",cols="3,10",options="header"] -!========================================================= -!Option !Description -! _pairs_ !A set of key/value alias/binding pairs to apply to requests. -!`reset` !Clears any aliases that were supplied in previous configurations of the remote. -!`show` !Shows the current set of aliases which is returned as a `Map` -!========================================================= -|timeout |Specifies the length of time in milliseconds the Console will wait for a response from the server. Specify -"none" to have no timeout. By default, this setting uses "none". -|========================================================= - -[[console-aliases]] -==== Aliases - -The `alias` configuration command for the Gremlin Server `:remote` can be useful in situations where there are -multiple `Graph` or `TraversalSource` instances on the server, as it becomes possible to rename them from the client -for purposes of execution within the context of a script. Therefore, it becomes possible to submit commands this way: - -[gremlin-groovy] ----- -:remote connect tinkerpop.server conf/remote-objects.yaml -:remote config alias x g -:> x.E().label().groupCount() -:remote close ----- +g = traversal().with('conf/remote-graph.properties') -[[console-sessions]] -==== Sessions - -A `:remote` created in the following fashion will be "sessionless", meaning each script issued to the server with -`:>` will be encased in a transaction and no state will be maintained from one request to the next. +g.V().values('name') +g.V().has('name','marko').out('created').values('name') +g.E().label().groupCount() -[source,groovy] ----- -gremlin> :remote connect tinkerpop.server conf/remote-objects.yaml -==>Configured localhost/127.0.0.1:8182 +g.close() ---- -In other words, the transaction will be automatically committed (or rolledback on error) and any variables declared -in that script will be forgotten for the next request. See the section on <> -for more information on that topic. +One can also construct remote traversal source via DRC, as well as via client to submit scripts. -To enable the remote to connect with a session the `connect` argument takes another argument as follows: - -[gremlin-groovy] +[source, text] ---- -:remote connect tinkerpop.server conf/remote.yaml session -:> x = 1 -:> y = 2 -:> x + y -:remote close ----- - -With the above command a session gets created with a random UUID for a session identifier. It is also possible to -assign a custom session identifier by adding it as the last argument to `:remote` command above. There is also the -option to replace "session" with "session-managed" to create a session that will auto-manage transactions (i.e. each -request will occur within the bounds of a transaction). In this way, the state of bound variables between requests are -maintained, but the need to manually managed the transactional scope of the graph is no longer required. +// with DRC +g = traversal().with(DriverRemoteConnection.using("localhost",8182,"g")) +g.V().count() -[[console-remote-console]] -==== Remote Console - -Previous examples have shown usage of the `:>` command to send scripts to Gremlin Server. The Gremlin Console also -supports an additional method for doing this which can be more convenient when the intention is to exclusively -work with a remote connection to the server. - -[gremlin-groovy] +// with client +client = Cluster.build("localhost").port(8182).create().connect() +client.submit("g.V().count()") ---- -:remote connect tinkerpop.server conf/remote.yaml session -:remote console -x = 1 -y = 2 -x + y -:remote console -:remote close ----- - -In the above example, the `:remote console` command is executed. It places the console in a state where the `:>` is -no longer required. Each script line is actually automatically submitted to Gremlin Server for evaluation. The -variables `x` and `y` that were defined actually don't exist locally - they only exist on the server! In this sense, -putting the console in this mode is basically like creating a window to a session on Gremlin Server. -TIP: When using `:remote console` there is not much point to using a configuration that uses a serializer that returns -actual data. In other words, using a configuration like the one inside of `conf/remote-objects.yaml` isn't typically -useful as in this mode the result will only ever be displayed but not used. Using a serializer configuration like -the one in `conf/remote.yaml` should perform better. - -NOTE: Console commands, those that begin with a colon (e.g. `:x`, `:remote`) do not execute remotely when in this mode. -They are all still evaluated locally. [[connecting-via-http]] === Connecting via HTTP @@ -1956,10 +1769,10 @@ With this configuration in place, a remote execution as follows, now times out r continuously: [source,groovy] -gremlin> :remote connect tinkerpop.server conf/remote.yaml -==>Configured localhost/127.0.0.1:8182 -gremlin> :> while(true) { } -==>Evaluation exceeded the configured 'evaluationTimeout' threshold of 30000 ms or evaluation was otherwise cancelled directly for request [while(true) {}] +client = Cluster.build("localhost").port(8182).create().connect() +==>org.apache.tinkerpop.gremlin.driver.Client$ClusteredClient@42ff9a77 +client.submit("while(true) { }") +org.apache.tinkerpop.gremlin.driver.exception.ResponseException: A timeout occurred during traversal evaluation of [RequestMessage{, fields={bindings={}, language=gremlin-groovy, batchSize=64}, gremlin=while(true) { }}] - consider increasing the limit given to evaluationTimeout The `GroovyCompilerGremlinPlugin` has a number of configuration options: @@ -2522,65 +2335,6 @@ Gremlin Server in this configuration should be more efficient when there are mul Gremlin Server will only close transactions on the graphs specified by the `aliases`. Keeping this setting `false`, will simply have Gremlin Server close transactions on all graphs for every request. -[[considering-state]] -==== Considering State - -With HTTP and any sessionless requests, there is no variable state maintained between requests. Therefore, -when <>, for example, it is not possible to create a variable in -one command and then expect to access it in the next: - -[source,groovy] ----- -gremlin> :remote connect tinkerpop.server conf/remote.yaml -==>Configured localhost/127.0.0.1:8182 -gremlin> :> x = 2 -==>2 -gremlin> :> 2 + x -No such property: x for class: Script4 -Display stack trace? [yN] n ----- - -The same behavior would be seen with HTTP or when using sessionless requests through one of the Gremlin Server drivers. -If having this behavior is desireable, then <>. - -There is an exception to this notion of state not existing between requests and that is globally defined functions. -All functions created via scripts are global to the server. - -[source,groovy] ----- -gremlin> :> def subtractIt(int x, int y) { x - y } -==>null -gremlin> :> subtractIt(8,7) -==>1 ----- - -If this behavior is not desirable there are several options. A first option would be to consider using sessions. Each -session gets its own `ScriptEngine`, which maintains its own isolated cache of global functions, whereas sessionless -requests uses a single function cache. A second option would be to define functions as closures: - -[source,groovy] ----- -gremlin> :> multiplyIt = { int x, int y -> x * y } -==>Script7$_run_closure1@6b24f3ab -gremlin> :> multiplyIt(7, 8) -No signature of method: org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.multiplyIt() is applicable for argument types: (java.lang.Integer, java.lang.Integer) values: [7, 8] -Display stack trace? [yN] ----- - -When the function is declared this way, the function is viewed by the `ScriptEngine` as a variable rather than a global -function and since sessionless requests don't maintain state, the function is forgotten for the next request. A final -option would be to manage the `ScriptEngine` cache manually: - -[source,bourne] ----- -$ curl -X POST -d "{\"gremlin\":\"def divideIt(int x, int y){ x / y }\",\"bindings\":{\"#jsr223.groovy.engine.keep.globals\":\"phantom\"}}" "http://localhost:8182" -{"requestId":"97fe1467-a943-45ea-8fd6-9e889a6c9381","status":{"message":"","code":200,"attributes":{}},"result":{"data":[null],"meta":{}}} -$ curl -X POST -d "{\"gremlin\":\"divideIt(8, 2)\"}" "http://localhost:8182" -{"message":"Error encountered evaluating script: divideIt(8, 2)"} ----- - -In the above HTTP-based requests, the bindings contain a special parameter that tells the `ScriptEngine` cache to -immediately forget the script after execution. In this way, the function does not end up being globally available. [[request-retry]] ==== Request Retry @@ -2648,147 +2402,6 @@ gremlin> :plugin use tinkerpop.credentials This plugin imports the appropriate classes for managing the credentials graph. -[[gephi-plugin]] -=== Gephi Plugin - -image:gephi-logo.png[width=200, float=left] link:http://gephi.org/[Gephi] is an interactive visualization, -exploration, and analysis platform for graphs. The link:https://gephi.org/plugins/#/plugin/graphstreaming[Graph Streaming] -plugin for Gephi provides an API that can be leveraged to stream graph data to a running Gephi application. The Gephi -plugin for Gremlin Console utilizes this API to allow for graph and traversal visualization. - -IMPORTANT: These instructions have been tested with Gephi 0.9.2 and Graph Streaming plugin 1.0.3. - -The following instructions assume that Gephi has been download and installed. It further assumes that the Graph -Streaming plugin has been installed (`Tools > Plugins`). The following instructions explain how to visualize a -`Graph` and `Traversal`. - -In Gephi, create a new project with `File > New Project`. In the lower left view, click the "Streaming" tab, open the -Master drop down, and right click `Master Server > Start` which starts the Graph Streaming server in Gephi and by -default accepts requests at `http://localhost:8080/workspace1`: - -image::gephi-start-server.png[width=800] - -IMPORTANT: The Gephi Streaming Plugin doesn't detect port conflicts and will appear to start the plugin successfully -even if there is something already active on that port it wants to connect to (which is 8080 by default). Be sure -that there is nothing running on the port before Gephi will be using before starting the plugin. Failing to do -this produce behavior where the console will appear to submit requests to Gephi successfully but nothing will -render. - -WARNING: Do not skip the `File > New Project` step as it may prevent a newly started Gephi application from fully -enabling the streaming tab. - -Start the xref:gremlin-console[Gremlin Console] and activate the Gephi plugin: - -[gremlin-groovy] ----- -:plugin use tinkerpop.gephi -graph = TinkerFactory.createModern() -:remote connect tinkerpop.gephi -:> graph ----- - -The above Gremlin session activates the Gephi plugin, creates the "modern" `TinkerGraph`, uses the `:remote` command -to setup a connection to the Graph Streaming server in Gephi (with default parameters that will be explained below), -and then uses `:submit` which sends the vertices and edges of the graph to the Gephi Streaming Server. The resulting -graph appears in Gephi as displayed in the left image below. - -image::gephi-graph-submit.png[width=800] - -NOTE: Issuing `:> graph` again will clear the Gephi workspace and then re-write the graph. To manually empty the -workspace do `:> clear`. - -Now that the graph is visualized in Gephi, it is possible to link:https://gephi.github.io/users/tutorial-layouts/[apply a layout algorithm], -change the size and/or color of vertices and edges, and display labels/properties of interest. Further information -can be found in Gephi's tutorial on link:https://gephi.github.io/users/tutorial-visualization/[Visualization]. -After applying the Fruchterman Reingold layout, increasing the node size, decreasing the edge scale, and displaying -the id, name, and weight attributes the graph looks as displayed in the right image above. - -Visualization of a `Traversal` has a different approach as the visualization occurs as the `Traversal` is executing, -thus showing a real-time view of its execution. A `Traversal` must be "configured" to operate in this format and for -that it requires use of the `visualTraversal` option on the `config` function of the `:remote` command: - -[gremlin-groovy,modern] ----- -:remote config visualTraversal graph <1> -traversal = vg.V(2).in().out('knows'). - has('age',gt(30)).outE('created'). - has('weight',gt(0.5d)).inV();[] <2> -:> traversal <3> ----- - -<1> Configure a "visual traversal" from your "graph" - this must be a `Graph` instance. This command will create a -new `TraversalSource` called "vg" that must be used to visualize any spawned traversals in Gephi. -<2> Define the traversal to be visualized. Note that ending the line with `;[]` simply prevents iteration of -the traversal before it is submitted. -<3> Submit the `Traversal` to visualize to Gephi. - -When the `:>` line is called, each step of the `Traversal` that produces or filters vertices generates events to -Gephi. The events update the color and size of the vertices at that step with `startRGBColor` and `startSize` -respectively. After the first step visualization, it sleeps for the configured `stepDelay` in milliseconds. On the -second step, it decays the configured `colorToFade` of all the previously visited vertices in prior steps, by -multiplying the current `colorToFade` value for each vertex with the `colorFadeRate`. Setting the `colorFadeRate` -value to `1.0` will prevent the color decay. The screenshots below show how the visualization evolves over the four -steps: - -image::gephi-traversal.png[width=1200] - -To get a sense of how the visualization configuration parameters affect the output, see the example below: - -[gremlin-groovy,modern] ----- -:remote config startRGBColor [0.0,0.3,1.0] -:remote config colorToFade b -:remote config colorFadeRate 0.5 -:> traversal ----- - -image::gephi-traversal-config.png[width=400] - -The visualization configuration above starts with a blue color now (most recently visited), fading the blue color -(so that dark green remains on oldest visited), and fading the blue color more quickly so that the gradient from dark -green to blue across steps has higher contrast. The following table provides a more detailed description of the -Gephi plugin configuration parameters as accepted via the `:remote config` command: - -[width="100%",cols="3,10,^2",options="header"] -|========================================================= -|Parameter |Description |Default -|workspace |The name of the workspace that your Graph Streaming server is started for. |workspace1 -|host |The host URL where the Graph Streaming server is configured for. |localhost -|port |The port number of the URL that the Graph Streaming server is listening on. |8080 -|sizeDecrementRate |The rate at which the size of an element decreases on each step of the visualization. |0.33 -|stepDelay |The amount of time in milliseconds to pause between step visualizations. |1000 -|startRGBColor |A size 3 float array of RGB color values which define the starting color to update most recently visited nodes with. |[0.0,1.0,0.5] -|startSize |The size an element should be when it is most recently visited. |20 -|colorToFade |A single char from the set `{r,g,b,R,G,B}` determining which color to fade for vertices visited in prior steps |g -|colorFadeRate |A float value in the range `(0.0,1.0]` which is multiplied against the current `colorToFade` value for prior vertices; a `1.0` value effectively turns off the color fading of prior step visited vertices |0.7 -|visualTraversal |Creates a `TraversalSource` variable in the Console named `vg` which can be used for visualizing traversals. This configuration option takes two parameters. The first is required and is the name of the `Graph` instance variable that will generate the `TraversalSource`. The second parameter is the variable name that the `TraversalSource` should have when referenced in the Console. If left unspecified, this value defaults to `vg`. |vg -|========================================================= - -NOTE: This plugin is typically only useful to the Gremlin Console and is enabled in the there by default. - -The instructions above assume that the `Graph` instance being visualized is local to the Gremlin Console. It makes that -assumption because the Gephi plugin requires a locally held `Graph`. If the intent is to visualize a `Graph` instance -hosted in Gremlin Server or a TinkerPop-enabled graph that can only be connected to in a "remote" fashion, then it -is still possible to use the Gephi plugin, but the requirement for a locally held `Graph` remains the same. To use -the Gephi plugin in these situations simply use <> to extract the portion of the remote -graph that will be visualized. Use of that step will return a `TinkerGraph` instance to the Gremlin Console at which -point it can be used locally with the Gephi plugin. The following example demonstrates the general steps: - -[source,text] ----- -gremlin> :remote connect tinkerpop.server conf/remote-objects.yaml <1> -... -gremlin> :> g.E().hasLabel('knows').subgraph('subGraph').cap('subGraph') <2> -... -gremlin> graph = result[0].object <3> -... ----- - -<1> Be sure to connect with a serializer configured to return objects and not their `toString()` representation which -is discussed in more detail in the <> Section. -<2> Use the `:>` command to subgraph the remote graph as needed. -<3> The `TinkerGraph` of that previous traversal can be found in the `result` object and now that the `Graph` is local -to Gremlin Console it can be used with Gephi as shown in the prior instruction set. [[graph-plugins]] === Graph Plugins diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index 7e4445bc5c1..120a1c1f990 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -732,7 +732,7 @@ objects and then re-use them. [gremlin-groovy] ---- -cluster = Cluster.open('conf/remote-objects.yaml') +cluster = Cluster.open('conf/remote.yaml') client = cluster.connect() g = traversal().with(DriverRemoteConnection.using(client, "g")) g.V().elementMap() diff --git a/docs/src/reference/implementations-neo4j.asciidoc b/docs/src/reference/implementations-neo4j.asciidoc index 174f93fcd8c..e5754c81dd3 100644 --- a/docs/src/reference/implementations-neo4j.asciidoc +++ b/docs/src/reference/implementations-neo4j.asciidoc @@ -56,7 +56,7 @@ Neo4j that TinkerPop currently supports in the `neo4j-tinkerpop-api-impl`. TIP: For configuring Grape, the dependency resolver of Groovy, please refer to the <> section. -[source,groovy] +[source,text] ---- gremlin> :install org.apache.tinkerpop neo4j-gremlin x.y.z ==>Loaded: [org.apache.tinkerpop, neo4j-gremlin, x.y.z] - restart the console to use [tinkerpop.neo4j] @@ -86,7 +86,25 @@ The Gremlin-Console session below demonstrates Neo4j indices. For more informati * Manipulating indices with link:http://neo4j.com/docs/developer-manual/current/#query-schema-index[Cypher]. * Manipulating indices with the Neo4j link:http://neo4j.com/docs/stable/tutorials-java-embedded-new-index.html[Java API]. -[gremlin-groovy] +[source,text] +---- +gremlin> graph = Neo4jGraph.open('/tmp/neo4j') +==>neo4jgraph[community single [/tmp/neo4j]] +gremlin> g = traversal().withEmbedded(graph) +==>graphtraversalsource[neo4jgraph[community single [/tmp/neo4j]], standard] +gremlin> graph.cypher("CREATE INDEX ON :person(name)") +gremlin> graph.tx().commit() //// (1) +==>null +gremlin> g.addV('person').property('name','marko') +==>v[0] +gremlin> g.addV('dog').property('name','puppy') +==>v[1] +gremlin> g.V().hasLabel('person').has('name','marko').values('name') +==>marko +gremlin> graph.close() +==>null +---- +[source,text] ---- graph = Neo4jGraph.open('/tmp/neo4j') g = traversal().with(graph) @@ -103,7 +121,33 @@ graph.close() Below demonstrates the runtime benefits of indices and demonstrates how if there is no defined index (only vertex labels), a linear scan of the vertex-label partition is still faster than a linear scan of all vertices. -[gremlin-groovy] +[source,text] +---- +gremlin> graph = Neo4jGraph.open('/tmp/neo4j') +==>neo4jgraph[community single [/tmp/neo4j]] +gremlin> g = traversal().withEmbedded(graph) +==>graphtraversalsource[neo4jgraph[community single [/tmp/neo4j]], standard] +gremlin> g.io('data/grateful-dead.xml').read().iterate() +gremlin> g.tx().commit() +==>null +gremlin> clock(1000) {g.V().hasLabel('artist').has('name','Garcia').iterate()} //// (1) +==>0.35031228 +gremlin> graph.cypher("CREATE INDEX ON :artist(name)") //// (2) +gremlin> g.tx().commit() +==>null +gremlin> Thread.sleep(5000) //// (3) +==>null +gremlin> clock(1000) {g.V().hasLabel('artist').has('name','Garcia').iterate()} //// (4) +==>0.061846079 +gremlin> clock(1000) {g.V().has('name','Garcia').iterate()} //// (5) +==>0.6680353569999999 +gremlin> graph.cypher("DROP INDEX ON :artist(name)") //// (6) +gremlin> g.tx().commit() +==>null +gremlin> graph.close() +==>null +---- +[source,text] ---- graph = Neo4jGraph.open('/tmp/neo4j') g = traversal().with(graph) @@ -134,7 +178,22 @@ image::gremlin-loves-cypher.png[width=400] NeoTechnology are the creators of the graph pattern-match query language link:https://neo4j.com/developer/cypher-query-language/[Cypher]. It is possible to leverage Cypher from within Gremlin by using the `Neo4jGraph.cypher()` graph traversal method. -[gremlin-groovy] +[source,text] +---- +gremlin> graph = Neo4jGraph.open('/tmp/neo4j') +==>neo4jgraph[community single [/tmp/neo4j]] +gremlin> g = traversal().withEmbedded(graph) +==>graphtraversalsource[neo4jgraph[community single [/tmp/neo4j]], standard] +gremlin> g.io('data/tinkerpop-modern.kryo').read().iterate() +gremlin> graph.cypher('MATCH (a {name:"marko"}) RETURN a') +==>[a:v[0]] +gremlin> graph.cypher('MATCH (a {name:"marko"}) RETURN a').select('a').out('knows').values('name') +==>josh +==>vadas +gremlin> graph.close() +==>null +---- +[source,text] ---- graph = Neo4jGraph.open('/tmp/neo4j') g = traversal().with(graph) @@ -165,7 +224,48 @@ public void removeLabel(String label) // remove a label from the vertex An example use case is presented below. -[gremlin-groovy] +[source,text] +---- +gremlin> graph = Neo4jGraph.open('/tmp/neo4j') +==>neo4jgraph[community single [/tmp/neo4j]] +gremlin> g = traversal().withEmbedded(graph) +==>graphtraversalsource[neo4jgraph[community single [/tmp/neo4j]], standard] +gremlin> vertex = (Neo4jVertex) g.addV('human::animal').next() //// (1) +==>v[0] +gremlin> vertex.label() //// (2) +==>animal::human +gremlin> vertex.labels() //// (3) +==>animal +==>human +gremlin> vertex.addLabel('organism') //// (4) +==>null +gremlin> vertex.label() +==>animal::human::organism +gremlin> vertex.removeLabel('human') //// (5) +==>null +gremlin> vertex.labels() +==>animal +==>organism +gremlin> vertex.addLabel('organism') //// (6) +==>null +gremlin> vertex.labels() +==>animal +==>organism +gremlin> vertex.removeLabel('human') //// (7) +==>null +gremlin> vertex.label() +==>animal::organism +gremlin> g.V().has(label,'organism') //// (8) +gremlin> g.V().has(label,of('organism')) //// (9) +==>v[0] +gremlin> g.V().has(label,of('organism')).has(label,of('animal')) +==>v[0] +gremlin> g.V().has(label,of('organism').and(of('animal'))) +==>v[0] +gremlin> graph.close() +==>null +---- +[source,text] ---- graph = Neo4jGraph.open('/tmp/neo4j') g = traversal().with(graph) diff --git a/docs/src/reference/implementations-spark.asciidoc b/docs/src/reference/implementations-spark.asciidoc index 0c9c61c4faa..2b3d6d0ca43 100644 --- a/docs/src/reference/implementations-spark.asciidoc +++ b/docs/src/reference/implementations-spark.asciidoc @@ -49,15 +49,7 @@ g.V().count() g.V().out().out().values('name') ---- -For using lambdas in Gremlin-Groovy, simply provide `:remote connect` a `TraversalSource` which leverages SparkGraphComputer. - -[gremlin-groovy] ----- -graph = GraphFactory.open('conf/hadoop/hadoop-gryo.properties') -g = traversal().with(graph).withComputer(SparkGraphComputer) -:remote connect tinkerpop.hadoop graph g -:> g.V().group().by{it.value('name')[1]}.by('name') ----- +NOTE: We will no longer support lambda executions via `:remote` on the Gremlin Console. The `SparkGraphComputer` algorithm leverages Spark's caching abilities to reduce the amount of data shuffled across the wire on each iteration of the <>. When the graph is loaded as a Spark RDD diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index bd4ea365c02..237e83eb15a 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -5602,26 +5602,27 @@ g.withStrategies(EdgeLabelVerificationStrategy(throwException=true)) === ElementIdStrategy -`ElementIdStrategy` provides control over element identifiers. Some Graph implementations, such as TinkerGraph, +`ElementIdStrategy` provides control over element identifiers. Some Graph implementations, such as `TinkerGraph`, allow specification of custom identifiers when creating elements: [gremlin-groovy] ---- -g = traversal().with(TinkerGraph.open()) +g = traversal().withEmbedded(TinkerGraph.open()) v = g.addV().property(id,'42a').next() g.V('42a') ---- -Other `Graph` implementations, such as Neo4j, generate element identifiers automatically and cannot be assigned. +Other `Graph` implementations generate element identifiers automatically and cannot be assigned. As a helper, `ElementIdStrategy` can be used to make identifier assignment possible by using vertex and edge indices -under the hood. +under the hood. Note that this demonstration is using `TinkerGraph` for convenience, however in practice +`ElementIdStrategy` offers little value in a `Graph` which allows custom identifiers. [gremlin-groovy] ---- -graph = Neo4jGraph.open('/tmp/neo4j') strategy = ElementIdStrategy.build().create() -g = traversal().with(graph).withStrategies(strategy) +g = traversal().withEmbedded(TinkerGraph.open()).withStrategies(strategy) g.addV().property(id, '42a').id() +g.V().elementMap() ---- IMPORTANT: The key that is used to store the assigned identifier should be indexed in the underlying graph diff --git a/gremlin-server/conf/gremlin-server.yaml b/gremlin-server/conf/gremlin-server.yaml index 08650bd0923..f0315247b52 100644 --- a/gremlin-server/conf/gremlin-server.yaml +++ b/gremlin-server/conf/gremlin-server.yaml @@ -26,7 +26,8 @@ scriptEngines: { plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {}, org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, - org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}} + org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}, + org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin: {enableThreadInterrupt: true}}}} serializers: - { className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV4, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }} # application/json - { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4 } # application/vnd.graphbinary-v1.0