This is an automated email from the ASF dual-hosted git repository. dkuppitz pushed a commit to branch TINKERPOP-2059 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 8ec155da10f6b8e7eb384a8dcce0b66928bde904 Author: Daniel Kuppitz <daniel_kupp...@hotmail.com> AuthorDate: Tue Oct 23 13:04:20 2018 -0700 TINKERPOP-2059 Implemented `by()` and `with()` modulators for `valueMap()` step. --- CHANGELOG.asciidoc | 1 + docs/src/recipes/collections.asciidoc | 6 +- docs/src/recipes/duplicate-edge.asciidoc | 4 +- docs/src/recipes/edge-move.asciidoc | 4 +- docs/src/reference/gremlin-applications.asciidoc | 10 +- docs/src/reference/gremlin-variants.asciidoc | 10 +- docs/src/reference/the-traversal.asciidoc | 29 ++- docs/src/upgrade/release-3.4.x.asciidoc | 49 +++++ .../tinkerpop/gremlin/jsr223/CoreImports.java | 2 + .../traversal/dsl/graph/GraphTraversal.java | 3 +- .../gremlin/process/traversal/dsl/graph/__.java | 3 +- .../traversal/step/map/PropertyMapStep.java | 135 +++++++++---- .../process/traversal/step/util/WithOptions.java | 70 +++++++ .../strategy/decoration/PartitionStrategy.java | 4 +- .../traversal/step/map/PropertyMapStepTest.java | 11 +- gremlin-dotnet/glv/WithOptions.template | 47 +++++ gremlin-dotnet/glv/generate.groovy | 21 +- .../Gremlin.Net/Process/Traversal/WithOptions.cs | 65 ++++++ .../TraversalEvaluationTests.cs | 4 +- .../Gherkin/TraversalEvaluation/TraversalParser.cs | 9 +- .../TraversalEvaluation/WithOptionsParameter.cs | 83 ++++++++ gremlin-javascript/glv/TraversalSource.template | 5 + gremlin-javascript/glv/generate.groovy | 11 +- .../main/javascript/gremlin-javascript/index.js | 3 +- .../gremlin-javascript/lib/process/traversal.js | 11 + .../test/cucumber/feature-steps.js | 3 +- gremlin-python/glv/TraversalSource.template | 8 + gremlin-python/glv/generate.groovy | 9 +- .../gremlin/python/jsr223/SymbolHelper.java | 1 + .../jython/gremlin_python/process/traversal.py | 20 ++ .../gremlin_python/structure/io/graphsonV2d0.py | 2 +- .../gremlin_python/structure/io/graphsonV3d0.py | 2 +- .../src/main/jython/radish/feature_steps.py | 5 +- gremlin-test/features/map/AddVertex.feature | 4 +- gremlin-test/features/map/ValueMap.feature | 43 +++- .../process/computer/GraphComputerTest.java | 2 +- .../process/traversal/step/map/AddVertexTest.java | 19 +- .../process/traversal/step/map/PageRankTest.java | 16 +- .../traversal/step/map/PeerPressureTest.java | 16 +- .../process/traversal/step/map/ProgramTest.java | 8 +- .../process/traversal/step/map/ValueMapTest.java | 221 +++++++++++++++------ .../tinkergraph/structure/TinkerGraphPlayTest.java | 120 +++-------- 42 files changed, 817 insertions(+), 282 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 402ed7c..aed87ed 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -25,6 +25,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima This release also includes changes from <<release-3-3-3, 3.3.3>>. +* Made `valueMap()` aware of `by` and `with` modulators and deprecated `valueMap(boolean)` overloads. * Use `Compare.eq` in `Contains` predicates to ensure the same filter behavior for numeric values. * Added `OptionsStrategy` to allow traversals to take arbitrary traversal-wide configurations. * Added text predicates. diff --git a/docs/src/recipes/collections.asciidoc b/docs/src/recipes/collections.asciidoc index ce237ec..ff76925 100644 --- a/docs/src/recipes/collections.asciidoc +++ b/docs/src/recipes/collections.asciidoc @@ -352,14 +352,14 @@ g.V(). has('name','marko'). union(project('degree'). <1> by(bothE().count()), - valueMap(true)). + valueMap().with(WithOptions.tokens)). unfold(). <2> group(). by(keys). by(select(values).unfold()) ---- -<1> The `valueMap(true)` of a `Vertex` can be extended with the "degree" of the `Vertex` by performing a `union()` of +<1> The `valueMap().with(WithOptions.tokens)` of a `Vertex` can be extended with the "degree" of the `Vertex` by performing a `union()` of the two traversals that produce that output (both produce `Map` objects). <2> The `unfold()`-step is used to decompose the `Map` objects into key/value entries that are then constructed back into a single new `Map` given the patterns shown earlier. The `Map` objects of both traversals in the `union()` are @@ -375,7 +375,7 @@ a `List` back for all the values of the new `Map`. With some added logic the `Ma ---- g.V(). has('name','marko'). - union(valueMap(true), + union(valueMap().with(WithOptions.tokens), project('age'). by(constant(100))). unfold(). diff --git a/docs/src/recipes/duplicate-edge.asciidoc b/docs/src/recipes/duplicate-edge.asciidoc index 27eae2b..710c6b5 100644 --- a/docs/src/recipes/duplicate-edge.asciidoc +++ b/docs/src/recipes/duplicate-edge.asciidoc @@ -133,7 +133,7 @@ demonstration, an additional edge is added to the "modern" graph: ---- g.V(1).as("a").V(3).addE("created").property("weight",0.4d).from("a").iterate() g.V(1).as("a").V(3).addE("created").property("weight",0.5d).from("a").iterate() -g.V(1).outE("created").valueMap(true) +g.V(1).outE("created").valueMap().with(WithOptions.tokens) ---- To identify the duplicate with this revised definition, the previous traversal can be modified to: @@ -147,5 +147,5 @@ g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as("ov"). where(neq("e1")). where(outV().as("ov")).as("e2"). where("e1", eq("e2")).by(label). - where("e1", eq("e2")).by("weight").valueMap(true) + where("e1", eq("e2")).by("weight").valueMap().with(WithOptions.tokens) ---- diff --git a/docs/src/recipes/edge-move.asciidoc b/docs/src/recipes/edge-move.asciidoc index f31b060..02b6911 100644 --- a/docs/src/recipes/edge-move.asciidoc +++ b/docs/src/recipes/edge-move.asciidoc @@ -28,7 +28,7 @@ follows: ---- g.V().has('name','marko'). outE().inV(). - path().by('name').by(valueMap(true)) + path().by('name').by(valueMap().with(WithOptions.tokens)) ---- The "marko" vertex contains a "knows" edge to the "vadas" vertex. The following code shows how to "move" that edge to @@ -47,7 +47,7 @@ g.V().has('name','marko').as('a'). select('e1').drop() <4> g.V().has('name','marko'). outE().inV(). - path().by('name').by(valueMap(true)) + path().by('name').by(valueMap().with(WithOptions.tokens)) ---- <1> Find the edge to "move" and label that as "e1". It will be necessary to reference this later to get the edge diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc index e82fccf..5e93b81 100644 --- a/docs/src/reference/gremlin-applications.asciidoc +++ b/docs/src/reference/gremlin-applications.asciidoc @@ -1063,7 +1063,7 @@ the `TraversalSource` be generated with `EmptyGraph` as follows: ---- graph = EmptyGraph.instance() g = graph.traversal().withRemote('conf/remote-graph.properties') -g.V().valueMap(true) +g.V().valueMap().with(WithOptions.tokens) g.close() ---- @@ -1079,7 +1079,7 @@ then re-use it. cluster = Cluster.open('conf/remote-objects.yaml') graph = EmptyGraph.instance() g = graph.traversal().withRemote(DriverRemoteConnection.using(cluster, "g")) -g.V().valueMap(true) +g.V().valueMap().with(WithOptions.tokens) g.close() cluster.close() ---- @@ -1894,7 +1894,7 @@ may still be consumed after the timeout if interruption does not succeed on the possible existence of multi-properties and meta-properties. When returning data from Gremlin Server only return the data that is required. For example, if only two properties of a `Vertex` are needed then simply return the two rather than returning the entire `Vertex` object itself. Even with an entire `Vertex`, it is typically much faster to issue -the query as `g.V(1).valueMap(true)` than `g.V(1)`, as the former returns a `Map` of the same data as a `Vertex`, but +the query as `g.V(1).valueMap().with(WithOptions.tokens)` than `g.V(1)`, as the former returns a `Map` of the same data as a `Vertex`, but without all the associated structure which can slow the response. [[parameterized-scripts]] @@ -1982,12 +1982,12 @@ There is no difference if re-written using the best practice of requesting only ---- Cluster cluster = Cluster.open(); Client client = cluster.connect(); -ResultSet results = client.submit("g.V().hasLabel('person').valueMap(true,'name')"); +ResultSet results = client.submit("g.V().hasLabel('person').valueMap('name').with(WithOptions.tokens)"); Graph graph = EmptyGraph.instance(); GraphTraversalSource g = graph.traversal(). withRemote('conf/remote-graph.properties'); -List<Vertex> results = g.V().hasLabel("person").valueMap(true,'name').toList(); +List<Vertex> results = g.V().hasLabel("person").valueMap('name').with(WithOptions.tokens).toList(); ---- Both of the above requests return a list of `Map` instances that contain the `id`, `label` and the "name" property. diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index 8c5f8f2..1d85426 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -261,13 +261,13 @@ In order to add and remove <<traversalstrategy,traversal strategies>> from a tra ---- g = g.withStrategies(SubgraphStrategy(vertices=hasLabel('person'),edges=has('weight',gt(0.5)))) g.V().name.toList() -g.V().outE().valueMap(True).toList() +g.V().outE().valueMap().with_(WithOptions.tokens).toList() g = g.withoutStrategies(SubgraphStrategy) g.V().name.toList() -g.V().outE().valueMap(True).toList() +g.V().outE().valueMap().with_(WithOptions.tokens).toList() g = g.withComputer(workers=2,vertices=has('name','marko')) g.V().name.toList() -g.V().outE().valueMap(True).toList() +g.V().outE().valueMap().with_(WithOptions.tokens).toList() ---- NOTE: Many of the `TraversalStrategy` classes in Gremlin-Python are proxies to the respective strategy on @@ -425,14 +425,14 @@ 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(); +var edgeValueMaps = g.V().OutE().ValueMap().With(WithOptions.Tokens).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 = g.V().OutE().ValueMap().With(WithOptions.Tokens).ToList(); // edgeValueMaps: [[label:created, id:9, weight:0.4], [label:knows, id:7, weight:0.5], [label:knows, id:8, weight:1.0]] ---- diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index 0fb7006..98f8d13 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -758,7 +758,7 @@ has been seen or not. [gremlin-groovy,modern] ---- -g.V().valueMap(true, 'name') +g.V().valueMap('name').with(WithOptions.tokens) g.V().dedup().by(label).values('name') ---- @@ -1792,7 +1792,7 @@ The term `not` is a reserved word in Python, and therefore must be referred to i [gremlin-groovy,modern] ---- -g.V().not(hasLabel('person')).valueMap(true) +g.V().not(hasLabel('person')).valueMap().with(WithOptions.tokens) g.V().hasLabel('person'). not(out('created').count().is(gt(1))).values('name') <1> ---- @@ -3170,7 +3170,7 @@ g.E().valueMap() It is important to note that the map of a vertex maintains a list of values for each key. The map of an edge or vertex-property represents a single property (not a list). The reason is that vertices in TinkerPop leverage -<<vertex-properties,vertex properties>> which are support multiple values per key. Using the <<the-crew-toy-graph, +<<vertex-properties,vertex properties>> which support multiple values per key. Using the <<the-crew-toy-graph, "The Crew">> toy graph, the point is made explicit. [gremlin-groovy,theCrew] @@ -3180,19 +3180,26 @@ g.V().has('name','marko').properties('location') g.V().has('name','marko').properties('location').valueMap() ---- -If the `id`, `label`, `key`, and `value` of the `Element` is desired, then a boolean triggers its insertion into the -returned map. +To turn list of values into single items, the `by()` modulator can be used as shown below. [gremlin-groovy,theCrew] ---- -g.V().hasLabel('person').valueMap(true) -g.V().hasLabel('person').valueMap(true,'name') -g.V().hasLabel('person').properties('location').valueMap(true) +g.V().valueMap().by(unfold()) +g.V().valueMap('name','location').by().by(unfold()) +---- + +If the `id`, `label`, `key`, and `value` of the `Element` is desired, then the `with()` modulator can be used to +trigger its insertion into the returned map. + +[gremlin-groovy,theCrew] +---- +g.V().hasLabel('person').valueMap().with(WithOptions.tokens) +g.V().hasLabel('person').valueMap('name').with(WithOptions.tokens, WithOptions.labels) +g.V().hasLabel('person').properties('location').valueMap().with(WithOptions.tokens, WithOptions.values) ---- *Additional References* -link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#valueMap-boolean-java.lang.String...-++[`valueMap(boolean,String...)`], link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#valueMap-java.lang.String...-++[`valueMap(String...)`] [values-step]] @@ -3860,8 +3867,8 @@ g = graph.traversal().withStrategies(SubgraphStrategy.build(). vertices(or(hasNot('location'),properties('location').count().is(gt(3)))). edges(hasLabel('develops')). vertexProperties(or(hasLabel(neq('location')),hasNot('endTime'))).create()) -g.V().valueMap(true) -g.E().valueMap(true) +g.V().valueMap().with(WithOptions.tokens) +g.E().valueMap().with(WithOptions.tokens) g.V().outE().inV(). path(). by('name'). diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc index d412fda..83bb9e6 100644 --- a/docs/src/upgrade/release-3.4.x.asciidoc +++ b/docs/src/upgrade/release-3.4.x.asciidoc @@ -29,6 +29,55 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.4.0/CHANGELOG.asc === Upgrading for Users +==== Modulation of valueMap() + +The `valueMap()` step now supports `by` and `with` modulation, which also led to the deprecation of `valueMap(true)` overloads. + +===== by() modulation + +===== with() modulation + +Thw `with()` modulator can be used to include certain tokens (`id`, `label`, `key` and/or `value`). + +The old way (still valid, but deprecated): + +``` +gremlin> g.V().hasLabel("software").valueMap(true) +==>[id:10,label:software,name:[gremlin]] +==>[id:11,label:software,name:[tinkergraph]] +gremlin> g.V().has("person","name","marko").properties("location").valueMap(true) +==>[id:6,key:location,value:san diego,startTime:1997,endTime:2001] +==>[id:7,key:location,value:santa cruz,startTime:2001,endTime:2004] +==>[id:8,key:location,value:brussels,startTime:2004,endTime:2005] +==>[id:9,key:location,value:santa fe,startTime:2005] +``` + +The new way: + +``` +gremlin> g.V().hasLabel("software").valueMap().with(WithOptions.tokens) +==>[id:10,label:software,name:[gremlin]] +==>[id:11,label:software,name:[tinkergraph]] +gremlin> g.V().has("person","name","marko").properties("location").valueMap().with(WithOptions.tokens) +==>[id:6,key:location,value:san diego,startTime:1997,endTime:2001] +==>[id:7,key:location,value:santa cruz,startTime:2001,endTime:2004] +==>[id:8,key:location,value:brussels,startTime:2004,endTime:2005] +==>[id:9,key:location,value:santa fe,startTime:2005] +``` + +Furthermore there now a finer control over which of the tokens should be included: + +``` +gremlin> g.V().hasLabel("software").valueMap().with(WithOptions.tokens, WithOptions.labels) +==>[label:software,name:[gremlin]] +==>[label:software,name:[tinkergraph]] +gremlin> g.V().has("person","name","marko").properties("location").valueMap().with(WithOptions.tokens, WithOptions.values) +==>[value:san diego,startTime:1997,endTime:2001] +==>[value:santa cruz,startTime:2001,endTime:2004] +==>[value:brussels,startTime:2004,endTime:2005] +==>[value:santa fe,startTime:2005] +``` + ==== Changed number comparison behavior of within()/without() predicates In previous versions `within()` and `without()` performed strict number comparisons; that means these predicates did not only compare number values, but also the time. This was inconsistent with diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java index 6ccf2e8..59f8a22 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java @@ -74,6 +74,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy; @@ -184,6 +185,7 @@ public final class CoreImports { CLASS_IMPORTS.add(TraversalOptionParent.Pick.class); CLASS_IMPORTS.add(P.class); CLASS_IMPORTS.add(TextP.class); + CLASS_IMPORTS.add(WithOptions.class); // remote CLASS_IMPORTS.add(RemoteConnection.class); CLASS_IMPORTS.add(RemoteGraph.class); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index e984312..fc24459 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -560,7 +560,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#valuemap-step" target="_blank">Reference Documentation - ValueMap Step</a> * @since 3.0.0-incubating */ - public default <E2> GraphTraversal<S, Map<String, E2>> valueMap(final String... propertyKeys) { + public default <E2> GraphTraversal<S, Map<Object, E2>> valueMap(final String... propertyKeys) { this.asAdmin().getBytecode().addStep(Symbols.valueMap, propertyKeys); return this.asAdmin().addStep(new PropertyMapStep<>(this.asAdmin(), false, PropertyType.VALUE, propertyKeys)); } @@ -576,6 +576,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#valuemap-step" target="_blank">Reference Documentation - ValueMap Step</a> * @since 3.0.0-incubating */ + @Deprecated public default <E2> GraphTraversal<S, Map<Object, E2>> valueMap(final boolean includeTokens, final String... propertyKeys) { this.asAdmin().getBytecode().addStep(Symbols.valueMap, includeTokens, propertyKeys); return this.asAdmin().addStep(new PropertyMapStep<>(this.asAdmin(), includeTokens, PropertyType.VALUE, propertyKeys)); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java index 02eee79..4361f29 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java @@ -256,13 +256,14 @@ public class __ { /** * @see GraphTraversal#valueMap(String...) */ - public static <A extends Element, B> GraphTraversal<A, Map<String, B>> valueMap(final String... propertyKeys) { + public static <A extends Element, B> GraphTraversal<A, Map<Object, B>> valueMap(final String... propertyKeys) { return __.<A>start().valueMap(propertyKeys); } /** * @see GraphTraversal#valueMap(boolean, String...) */ + @Deprecated public static <A extends Element, B> GraphTraversal<A, Map<Object, B>> valueMap(final boolean includeTokens, final String... propertyKeys) { return __.<A>start().valueMap(includeTokens, propertyKeys); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java index 9ac7d3d..0357e87 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java @@ -20,8 +20,13 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating; +import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; @@ -34,67 +39,117 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @author Daniel Kuppitz (http://gremlin.guru) */ -public class PropertyMapStep<K,E> extends MapStep<Element, Map<K, E>> implements TraversalParent { +public class PropertyMapStep<K,E> extends MapStep<Element, Map<K, E>> + implements TraversalParent, ByModulating, Configuring { protected final String[] propertyKeys; protected final PropertyType returnType; - protected final boolean includeTokens; + + protected int tokens; protected Traversal.Admin<Element, ? extends Property> propertyTraversal; + private Parameters parameters = new Parameters(); + private TraversalRing<K, E> traversalRing; + + @Deprecated public PropertyMapStep(final Traversal.Admin traversal, final boolean includeTokens, final PropertyType propertyType, final String... propertyKeys) { + this(traversal, propertyType, propertyKeys); + this.configure(WithOptions.tokens, includeTokens ? WithOptions.all : WithOptions.none); + } + + public PropertyMapStep(final Traversal.Admin traversal, final PropertyType propertyType, final String... propertyKeys) { super(traversal); - this.includeTokens = includeTokens; this.propertyKeys = propertyKeys; this.returnType = propertyType; this.propertyTraversal = null; + this.traversalRing = new TraversalRing<>(); } @Override protected Map<K, E> map(final Traverser.Admin<Element> traverser) { - final Map<Object, Object> map = new HashMap<>(); + final Map<Object, Object> map = new LinkedHashMap<>(); final Element element = traverser.get(); - final boolean isVertex = traverser.get() instanceof Vertex; + final boolean isVertex = element instanceof Vertex; + if (this.returnType == PropertyType.VALUE) { + if (includeToken(WithOptions.ids)) map.put(T.id, element.id()); + if (element instanceof VertexProperty) { + if (includeToken(WithOptions.keys)) map.put(T.key, ((VertexProperty<?>) element).key()); + if (includeToken(WithOptions.values)) map.put(T.value, ((VertexProperty<?>) element).value()); + } else { + if (includeToken(WithOptions.labels)) map.put(T.label, element.label()); + } + } final Iterator<? extends Property> properties = null == this.propertyTraversal ? element.properties(this.propertyKeys) : TraversalUtil.applyAll(traverser, this.propertyTraversal); + //final Iterator<? extends Property> properties = element.properties(this.propertyKeys); while (properties.hasNext()) { final Property<?> property = properties.next(); + final Object value = this.returnType == PropertyType.VALUE ? property.value() : property; if (isVertex) { - List<Object> values = (List<Object>) map.get(property.key()); - if (null == values) { - values = new ArrayList<>(); - map.put(property.key(), values); - } - values.add(this.returnType == PropertyType.VALUE ? property.value() : property); - } else - map.put(property.key(), this.returnType == PropertyType.VALUE ? property.value() : property); - } - if (this.returnType == PropertyType.VALUE && this.includeTokens) { - // add tokens, as string keys - if (element instanceof VertexProperty) { - map.put(T.id, element.id()); - map.put(T.key, ((VertexProperty<?>) element).key()); - map.put(T.value, ((VertexProperty<?>) element).value()); + map.compute(property.key(), (k, v) -> { + final List<Object> values = v != null ? (List<Object>) v : new ArrayList<>(); + values.add(value); + return values; + }); } else { - map.put(T.id, element.id()); - map.put(T.label, element.label()); + map.put(property.key(), value); } } + if (!traversalRing.isEmpty()) { + for (final Object key : map.keySet()) { + map.compute(key, (k, v) -> TraversalUtil.applyNullable(v, (Traversal.Admin) this.traversalRing.next())); + } + this.traversalRing.reset(); + } return (Map) map; } @Override - public List<Traversal.Admin<Element, ? extends Property>> getLocalChildren() { - return null == this.propertyTraversal ? Collections.emptyList() : Collections.singletonList(this.propertyTraversal); + public void configure(final Object... keyValues) { + if (keyValues[0].equals(WithOptions.tokens)) { + if (keyValues.length == 2 && keyValues[1] instanceof Boolean) { + this.tokens = ((boolean) keyValues[1]) ? WithOptions.all : WithOptions.none; + } else { + for (int i = 1; i < keyValues.length; i++) { + if (!(keyValues[i] instanceof Integer)) + throw new IllegalArgumentException("WithOptions.tokens requires Integer arguments (possible " + "" + + "values are: WithOptions.[none|ids|labels|keys|values|all])"); + this.tokens |= (int) keyValues[i]; + } + } + } else { + this.parameters.set(this, keyValues); + } + } + + @Override + public Parameters getParameters() { + return parameters; + } + + @Override + public List<Traversal.Admin<K, E>> getLocalChildren() { + final List<Traversal.Admin<K, E>> result = new ArrayList<>(); + if (null != this.propertyTraversal) + result.add((Traversal.Admin) propertyTraversal); + result.addAll(this.traversalRing.getTraversals()); + return Collections.unmodifiableList(result); + } + + @Override + public void modulateBy(final Traversal.Admin<?, ?> selectTraversal) { + this.traversalRing.addTraversal(this.integrateChild(selectTraversal)); } public void setPropertyTraversal(final Traversal.Admin<Element, ? extends Property> propertyTraversal) { @@ -109,14 +164,14 @@ public class PropertyMapStep<K,E> extends MapStep<Element, Map<K, E>> implements return propertyKeys; } + @Deprecated public boolean isIncludeTokens() { - return includeTokens; + return this.tokens != WithOptions.none; } public String toString() { - return null != this.propertyTraversal ? - StringFactory.stepString(this, this.propertyTraversal, this.returnType.name().toLowerCase()) : - StringFactory.stepString(this, Arrays.asList(this.propertyKeys), this.returnType.name().toLowerCase()); + return StringFactory.stepString(this, Arrays.asList(this.propertyKeys), + this.traversalRing, this.returnType.name().toLowerCase()); } @Override @@ -124,20 +179,19 @@ public class PropertyMapStep<K,E> extends MapStep<Element, Map<K, E>> implements final PropertyMapStep<K,E> clone = (PropertyMapStep<K,E>) super.clone(); if (null != this.propertyTraversal) clone.propertyTraversal = this.propertyTraversal.clone(); + clone.traversalRing = this.traversalRing.clone(); return clone; } @Override public int hashCode() { - int result = super.hashCode() ^ this.returnType.hashCode() ^ Boolean.hashCode(this.includeTokens); - if (null == this.propertyTraversal) { - for (final String propertyKey : this.propertyKeys) { - result ^= propertyKey.hashCode(); - } - } else { + int result = super.hashCode() ^ this.returnType.hashCode() ^ Integer.hashCode(this.tokens); + if (null != this.propertyTraversal) result ^= this.propertyTraversal.hashCode(); + for (final String propertyKey : this.propertyKeys) { + result ^= propertyKey.hashCode(); } - return result; + return result ^ this.traversalRing.hashCode(); } @Override @@ -145,10 +199,19 @@ public class PropertyMapStep<K,E> extends MapStep<Element, Map<K, E>> implements super.setTraversal(parentTraversal); if (null != this.propertyTraversal) this.integrateChild(this.propertyTraversal); + this.traversalRing.getTraversals().forEach(this::integrateChild); } @Override public Set<TraverserRequirement> getRequirements() { return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT); } + + public int getIncludedTokens() { + return this.tokens; + } + + private boolean includeToken(final int token) { + return 0 != (this.tokens & token); + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java new file mode 100644 index 0000000..3faa95d --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java @@ -0,0 +1,70 @@ +/* + * 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. + */ +package org.apache.tinkerpop.gremlin.process.traversal.step.util; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Graph; + +/** + * Configuration options to be passed to the {@link GraphTraversal#with(String, Object)}. + * + * @author Daniel Kuppitz (http://gremlin.guru) + */ +public class WithOptions { + + // + // PropertyMapStep + // + + /** + * Configures the tokens to be included in value maps. + */ + public static final String tokens = Graph.Hidden.hide("tinkerpop.valueMap.tokens"); + + /** + * Include no tokens. + */ + public static int none = 0; + + /** + * Include ids (affects all {@link org.apache.tinkerpop.gremlin.structure.Element} value maps). + */ + public static int ids = 1; + + /** + * Include labels (affects all {@link org.apache.tinkerpop.gremlin.structure.Vertex} and + * {@link org.apache.tinkerpop.gremlin.structure.Edge} value maps). + */ + public static int labels = 2; + + /** + * Include keys (affects all {@link org.apache.tinkerpop.gremlin.structure.VertexProperty} value maps). + */ + public static int keys = 4; + + /** + * Include keys (affects all {@link org.apache.tinkerpop.gremlin.structure.VertexProperty} value maps). + */ + public static int values = 8; + + /** + * Include all tokens. + */ + public static int all = ids | labels | keys | values; +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java index 15079cb..4059191 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java @@ -43,6 +43,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.structure.Element; @@ -180,7 +181,8 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal // as this is a value map, replace that step with propertiesMap() that returns PropertyType.VALUE. // from there, add the filter as shown above and then unwrap the properties as they would have // been done under valueMap() - final PropertyMapStep propertyMapStep = new PropertyMapStep(traversal, step.isIncludeTokens(), PropertyType.PROPERTY, step.getPropertyKeys()); + final PropertyMapStep propertyMapStep = new PropertyMapStep(traversal, PropertyType.PROPERTY, step.getPropertyKeys()); + propertyMapStep.configure(WithOptions.tokens, step.getIncludedTokens()); TraversalHelper.replaceStep(step, propertyMapStep, traversal); final LambdaMapStep mapPropertiesFilterStep = new LambdaMapStep<>(traversal, new MapPropertiesFilter()); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStepTest.java index cc223dc..c46950e 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStepTest.java @@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import java.util.Arrays; import java.util.List; @@ -37,10 +38,12 @@ public class PropertyMapStepTest extends StepTest { __.valueMap("name"), __.valueMap("age"), __.valueMap("name", "age"), - __.valueMap(true), - __.valueMap(true, "name"), - __.valueMap(true, "age"), - __.valueMap(true, "name", "age"), + __.valueMap().with(WithOptions.tokens), + __.valueMap("name").with(WithOptions.tokens), + __.valueMap("age").with(WithOptions.tokens), + __.valueMap("name", "age").with(WithOptions.tokens), + __.valueMap("name", "age").with(WithOptions.tokens, WithOptions.ids), + __.valueMap("name", "age").with(WithOptions.tokens, WithOptions.labels), __.propertyMap(), __.propertyMap("name"), __.propertyMap("age"), diff --git a/gremlin-dotnet/glv/WithOptions.template b/gremlin-dotnet/glv/WithOptions.template new file mode 100644 index 0000000..d8dd6bd --- /dev/null +++ b/gremlin-dotnet/glv/WithOptions.template @@ -0,0 +1,47 @@ +#region License + +/* + * 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. + */ + +#endregion + +// THIS IS A GENERATED FILE - DO NOT MODIFY THIS FILE DIRECTLY - see pom.xml +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Gremlin.Net.Process.Traversal.Step.Util +{ +#pragma warning disable 1591 + + /// <summary> + /// Configuration options to be passed to the <c>With()</c> modulator. + /// </summary> + public class WithOptions + { +<% withOptions.each { field -> %> + + public static readonly <%= field.type %> <%= field.name %> = <%= field.value %>; +<% } %> + } + +#pragma warning restore 1591 +} diff --git a/gremlin-dotnet/glv/generate.groovy b/gremlin-dotnet/glv/generate.groovy index 7985ca0..26daa58 100644 --- a/gremlin-dotnet/glv/generate.groovy +++ b/gremlin-dotnet/glv/generate.groovy @@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.P import org.apache.tinkerpop.gremlin.process.traversal.TextP import org.apache.tinkerpop.gremlin.process.traversal.IO import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__ +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions import org.apache.tinkerpop.gremlin.structure.Direction import java.lang.reflect.Modifier import java.lang.reflect.TypeVariable @@ -114,6 +115,10 @@ def toCSharpType = { name -> def toCSharpMethodName = { symbol -> (String) Character.toUpperCase(symbol.charAt(0)) + symbol.substring(1) } +def toCSharpValue = { type, value -> + type == String.class && value != null ? ('"' + value + '"') : value +} + def getJavaGenericTypeParameterTypeNames = { method -> def typeArguments = method.genericReturnType.actualTypeArguments return typeArguments. @@ -133,7 +138,7 @@ def getJavaParameterTypeNames = { method -> return method.parameters. collect { param -> param.type.simpleName - } + } } def toCSharpParamString = { param, genTypeName -> @@ -182,7 +187,7 @@ def getCSharpParamString = { method, useGenericParams -> else if (genType instanceof GenericArrayType) { if (((GenericArrayType)genType).getGenericComponentType() instanceof TypeVariable<?>) { genTypeName = ((TypeVariable<?>)((GenericArrayType)genType).getGenericComponentType()).name + "[]" - } + } } } toCSharpParamString(param, genTypeName) @@ -263,8 +268,8 @@ def binding = ["pmethods": P.class.getMethods(). return ["methodName": javaMethod.name, "parameters":parameters, "paramNames":paramNames] }, "sourceSpawnMethods": GraphTraversalSource.getMethods(). // SPAWN STEPS - findAll { GraphTraversal.class.equals(it.returnType) }. - // Select unique combination of C# parameter types and sort by Java parameter type combination + findAll { GraphTraversal.class.equals(it.returnType) }. + // Select unique combination of C# parameter types and sort by Java parameter type combination sort { a, b -> a.name <=> b.name ?: getJavaParamTypeString(a) <=> getJavaParamTypeString(b) }. unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }. collect { javaMethod -> @@ -321,7 +326,9 @@ def binding = ["pmethods": P.class.getMethods(). return ["methodName": javaMethod.name, "t2":t2, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "callGenericTypeArg":callGenericTypeArg, "graphTraversalT2":graphTraversalT2] }, "tokens": gatherTokensFrom([IO, ConnectedComponent, ShortestPath, PageRank, PeerPressure]), - "toCSharpMethodName": toCSharpMethodName] + "toCSharpMethodName": toCSharpMethodName, + "withOptions": WithOptions.getDeclaredFields(). + collect {["type": toCSharpType(it.type.simpleName), "name": toCSharpMethodName(it.name), "value": toCSharpValue(it.type, it.get(null))]}] def engine = new groovy.text.GStringTemplateEngine() def traversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/GraphTraversal.template")).make(binding) @@ -344,6 +351,10 @@ def tpTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/TextP.tem def tpFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/TextP.cs") tpFile.newWriter().withWriter{ it << tpTemplate } +def withOptionsTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/WithOptions.template")).make(binding) +def withOptionsFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/WithOptions.cs") +withOptionsFile.newWriter().withWriter{ it << withOptionsTemplate } + binding.tokens.each {k,v -> def tokenTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Token.template")).make([tokenFields: v, tokenName: k]) def tokenFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/${k}.cs") diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs new file mode 100644 index 0000000..8b79089 --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs @@ -0,0 +1,65 @@ +#region License + +/* + * 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. + */ + +#endregion + +// THIS IS A GENERATED FILE - DO NOT MODIFY THIS FILE DIRECTLY - see pom.xml +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Gremlin.Net.Process.Traversal.Step.Util +{ +#pragma warning disable 1591 + + /// <summary> + /// Configuration options to be passed to the <c>With()</c> modulator. + /// </summary> + public class WithOptions + { + + + public static readonly string Tokens = "~tinkerpop.valueMap.tokens"; + + + public static readonly int None = 0; + + + public static readonly int Ids = 1; + + + public static readonly int Labels = 2; + + + public static readonly int Keys = 4; + + + public static readonly int Values = 8; + + + public static readonly int All = 15; + + } + +#pragma warning restore 1591 +} diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs index 4e2bfff..081b6f6 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs @@ -87,7 +87,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation // Tuple.Create("g.V().constant(123L)", 2), // Can be parsed using the new type-safe API Tuple.Create("g.V().has(\"no\").count()", 3), Tuple.Create("g.V().values(\"age\")", 2), - Tuple.Create("g.V().valueMap(true, \"name\", \"age\")", 2), + Tuple.Create("g.V().valueMap(\"name\", \"age\").with(WithOptions.tokens)", 3), Tuple.Create("g.V().where(__.in(\"created\").count().is(1)).values(\"name\")", 3), Tuple.Create("g.V().count(Scope.local)", 2), Tuple.Create("g.V().values(\"age\").is(P.lte(30))", 3), @@ -111,4 +111,4 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation } } } -} \ No newline at end of file +} diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs index 8d44a57..105932f 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -46,6 +46,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation private static readonly Regex RegexIO = new Regex(@"IO.\w+", RegexOptions.Compiled); + private static readonly Regex RegexWithOptions = new Regex(@"WithOptions.\w+", RegexOptions.Compiled); + private static readonly Regex RegexParam = new Regex(@"\w+", RegexOptions.Compiled); private static readonly HashSet<Type> NumericTypes = new HashSet<Type> @@ -126,7 +128,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation var compatibleMethods = new Dictionary<int, MethodInfo>(); foreach (var method in ordered) { - lastMethod = method; var methodParameters = method.GetParameters(); var requiredParameters = methodParameters.Length; if (requiredParameters > 0 && IsParamsArray(methodParameters.Last())) @@ -138,6 +139,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { continue; } + lastMethod = method; var matched = true; var exactMatches = 0; for (var i = 0; i < tokenParameters.Count; i++) @@ -452,6 +454,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation i += parameterText.Length - 1; return new IOParameter(parameterText); } + if (RegexWithOptions.IsMatch(parameterText)) + { + i += parameterText.Length - 1; + return new WithOptionsParameter(parameterText); + } if (RegexEnum.IsMatch(parameterText)) { i += parameterText.Length - 1; diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/WithOptionsParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/WithOptionsParameter.cs new file mode 100644 index 0000000..9b055ec --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/WithOptionsParameter.cs @@ -0,0 +1,83 @@ +#region License + +/* + * 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. + */ + +#endregion + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using Gremlin.Net.Process.Traversal.Step.Util; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + /// <summary> + /// Represents a parameter for the with() step + /// </summary> + internal class WithOptionsParameter : ITokenParameter, IEquatable<WithOptionsParameter> + { + private readonly string _text; + private readonly string _value; + + public WithOptionsParameter(string text) + { + _text = text; + var separatorIndex = text.IndexOf('.'); + var value = text.Substring(separatorIndex + 1); + _value = value.Substring(0, 1).ToUpper() + value.Substring(1); + } + + public bool Equals(WithOptionsParameter other) + { + return _text == other._text; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((WithOptionsParameter) obj); + } + + public override int GetHashCode() + { + return _text.GetHashCode(); + } + + public object GetValue() + { + var field = typeof(WithOptions).GetField(_value, BindingFlags.Static | BindingFlags.Public); + return field.GetValue(null); + } + + public void SetContextParameterValues(IDictionary<string, object> parameterValues) + { + + } + + public Type GetParameterType() + { + return typeof(object); + } + } +} diff --git a/gremlin-javascript/glv/TraversalSource.template b/gremlin-javascript/glv/TraversalSource.template index cdbec26..c66cbb4 100644 --- a/gremlin-javascript/glv/TraversalSource.template +++ b/gremlin-javascript/glv/TraversalSource.template @@ -215,6 +215,10 @@ class TraversalSideEffects { } +const withOptions = { + <%= withOptions.collect { it.name + ": " + it.value }.join(",\n ")%> +}; + function toEnum(typeName, keys) { const result = {}; keys.split(' ').forEach(k => { @@ -242,6 +246,7 @@ module.exports = { EnumValue, P, TextP, + withOptions, IO, Traversal, TraversalSideEffects, diff --git a/gremlin-javascript/glv/generate.groovy b/gremlin-javascript/glv/generate.groovy index 575f847..cd3e899 100644 --- a/gremlin-javascript/glv/generate.groovy +++ b/gremlin-javascript/glv/generate.groovy @@ -31,6 +31,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.P import org.apache.tinkerpop.gremlin.process.traversal.TextP import org.apache.tinkerpop.gremlin.process.traversal.IO import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__ +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions import java.lang.reflect.Modifier def toJsMap = ["in": "in_", @@ -39,6 +40,10 @@ def toJsMap = ["in": "in_", def toJs = { symbol -> toJsMap.getOrDefault(symbol, symbol) } +def toJSValue = { type, value -> + type == String.class && value != null ? ('"' + value + '"') : value +} + def decapitalize = { String string = it; if (string == null || string.length() == 0) { @@ -109,7 +114,9 @@ def binding = ["enums": CoreImports.getClassImports() "tokens": gatherTokensFrom([IO, ConnectedComponent, ShortestPath, PageRank, PeerPressure]), "toJs": toJs, "version": determineVersion(), - "decapitalize": decapitalize] + "decapitalize": decapitalize, + "withOptions": WithOptions.getDeclaredFields(). + collect {["name": it.name, "value": toJSValue(it.type, it.get(null))]}] def engine = new GStringTemplateEngine() def graphTraversalTemplate = engine.createTemplate(new File("${project.basedir}/glv/GraphTraversalSource.template")) @@ -124,4 +131,4 @@ traversalFile.newWriter().withWriter{ it << traversalTemplate } def packageJsonTemplate = engine.createTemplate(new File("${project.basedir}/glv/PackageJson.template")).make(binding) def packageJsonFile = new File("${project.basedir}/src/main/javascript/gremlin-javascript/package.json") -packageJsonFile.newWriter().withWriter{ it << packageJsonTemplate } \ No newline at end of file +packageJsonFile.newWriter().withWriter{ it << packageJsonTemplate } diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js index 7f97b40..a7a9acd 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js @@ -73,7 +73,8 @@ module.exports = { GraphTraversal: gt.GraphTraversal, GraphTraversalSource: gt.GraphTraversalSource, statics: gt.statics, - Translator + Translator, + withOptions: t.withOptions }, structure: { io: { diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js index 81853a6..ddf58b0 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js @@ -388,6 +388,16 @@ class TraversalSideEffects { } +const withOptions = { + tokens: "~tinkerpop.valueMap.tokens", + none: 0, + ids: 1, + labels: 2, + keys: 4, + values: 8, + all: 15 +}; + function toEnum(typeName, keys) { const result = {}; keys.split(' ').forEach(k => { @@ -415,6 +425,7 @@ module.exports = { EnumValue, P, TextP, + withOptions, IO, Traversal, TraversalSideEffects, diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js index d1d5b52..5f63bff 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js @@ -225,7 +225,8 @@ function getSandbox(g, parameters) { Scope: traversalModule.scope, Operator: traversalModule.operator, T: traversalModule.t, - toLong: utils.toLong + toLong: utils.toLong, + WithOptions: traversalModule.withOptions }; // Pass the parameter to the sandbox Object.keys(parameters).forEach(paramName => sandbox[paramName] = parameters[paramName]); diff --git a/gremlin-python/glv/TraversalSource.template b/gremlin-python/glv/TraversalSource.template index 4f40a5f..5c623c0 100644 --- a/gremlin-python/glv/TraversalSource.template +++ b/gremlin-python/glv/TraversalSource.template @@ -344,3 +344,11 @@ class Binding(object): def __repr__(self): return "binding[" + self.key + "=" + str(self.value) + "]" + +''' +WITH OPTIONS +''' +class WithOptions(object): +<% withOptions.each { %> + <%= it.name %> = <%= it.value %> +<% } %> diff --git a/gremlin-python/glv/generate.groovy b/gremlin-python/glv/generate.groovy index 60d8381..6f61680 100644 --- a/gremlin-python/glv/generate.groovy +++ b/gremlin-python/glv/generate.groovy @@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.P import org.apache.tinkerpop.gremlin.process.traversal.TextP import org.apache.tinkerpop.gremlin.process.traversal.IO import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__ +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions import java.lang.reflect.Modifier // this is a bit of a copy of what's in SymbolHelper - no way around it because this code generation task occurs @@ -52,6 +53,10 @@ def gatherTokensFrom = { tokenClasses -> return m } +def toPythonValue = { type, value -> + type == String.class && value != null ? ('"' + value + '"') : value +} + def toJavaMap = toPythonMap.collectEntries{k,v -> [(v):k]} def toPython = { symbol -> toPythonMap.getOrDefault(symbol, symbol) } def toJava = { symbol -> toJavaMap.getOrDefault(symbol, symbol) } @@ -100,7 +105,9 @@ def binding = ["enums": CoreImports.getClassImports() sort { a, b -> a <=> b }, "tokens": gatherTokensFrom([IO, ConnectedComponent, ShortestPath, PageRank, PeerPressure]), "toPython": toPython, - "toJava": toJava] + "toJava": toJava, + "withOptions": WithOptions.getDeclaredFields(). + collect {["name": it.name, "value": toPythonValue(it.type, it.get(null))]}] def engine = new groovy.text.GStringTemplateEngine() def traversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/TraversalSource.template")).make(binding) diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/SymbolHelper.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/SymbolHelper.java index ce4ad7f..6a7564b 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/SymbolHelper.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/SymbolHelper.java @@ -42,6 +42,7 @@ public final class SymbolHelper { TO_PYTHON_MAP.put("list", "list_"); TO_PYTHON_MAP.put("set", "set_"); TO_PYTHON_MAP.put("all", "all_"); + TO_PYTHON_MAP.put("with", "with_"); // TO_PYTHON_MAP.forEach((k, v) -> FROM_PYTHON_MAP.put(v, k)); } diff --git a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py index c1458bc..13e4504 100644 --- a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py +++ b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py @@ -616,3 +616,23 @@ class Binding(object): def __repr__(self): return "binding[" + self.key + "=" + str(self.value) + "]" + +''' +WITH OPTIONS +''' +class WithOptions(object): + + tokens = "~tinkerpop.valueMap.tokens" + + none = 0 + + ids = 1 + + labels = 2 + + keys = 4 + + values = 8 + + all = 15 + diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py index cfb5171..3046f54 100644 --- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py +++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py @@ -136,7 +136,7 @@ class _GraphSONTypeIO(object): symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and", "or_": "or", "is_": "is", "not_": "not", "from_": "from", - "set_": "set", "list_": "list", "all_": "all"} + "set_": "set", "list_": "list", "all_": "all", "with_": "with"} @classmethod def unmangleKeyword(cls, symbol): diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py index f634361..aeaa6e1 100644 --- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py +++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py @@ -141,7 +141,7 @@ class _GraphSONTypeIO(object): symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and", "or_": "or", "is_": "is", "not_": "not", "from_": "from", - "set_": "set", "list_": "list", "all_": "all"} + "set_": "set", "list_": "list", "all_": "all", "with_": "with"} @classmethod def unmangleKeyword(cls, symbol): diff --git a/gremlin-python/src/main/jython/radish/feature_steps.py b/gremlin-python/src/main/jython/radish/feature_steps.py index 4777ef3..e206454 100644 --- a/gremlin-python/src/main/jython/radish/feature_steps.py +++ b/gremlin-python/src/main/jython/radish/feature_steps.py @@ -21,7 +21,7 @@ import json import re from gremlin_python.structure.graph import Graph, Path from gremlin_python.process.graph_traversal import __ -from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO +from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO, WithOptions from radish import given, when, then from hamcrest import * @@ -262,7 +262,8 @@ def _make_traversal(g, traversal_string, params): "Pop": Pop, "Scope": Scope, "Operator": Operator, - "T": T} + "T": T, + "WithOptions": WithOptions} b.update(params) diff --git a/gremlin-test/features/map/AddVertex.feature b/gremlin-test/features/map/AddVertex.feature index 761916e..a60fefb 100644 --- a/gremlin-test/features/map/AddVertex.feature +++ b/gremlin-test/features/map/AddVertex.feature @@ -336,7 +336,7 @@ Feature: Step - addV() | marko | And the graph should return 2 for count of "g.V().has(\"name\",\"marko\")" - Scenario: g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMapXtrueX + Scenario: g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX Given the empty graph And the graph initializer of """ @@ -355,7 +355,7 @@ Feature: Step - addV() """ And the traversal of """ - g.V().as("a").has("name", "marko").out("created").as("b").addV(__.select("a").label()).property("test", __.select("b").label()).valueMap(true) + g.V().as("a").has("name", "marko").out("created").as("b").addV(__.select("a").label()).property("test", __.select("b").label()).valueMap().with(WithOptions.tokens) """ When iterated to list Then the result should have a count of 1 diff --git a/gremlin-test/features/map/ValueMap.feature b/gremlin-test/features/map/ValueMap.feature index 109318f..e8880f8 100644 --- a/gremlin-test/features/map/ValueMap.feature +++ b/gremlin-test/features/map/ValueMap.feature @@ -33,11 +33,11 @@ Feature: Step - valueMap() | m[{"name": ["lop"], "lang": ["java"]}] | | m[{"name": ["ripple"], "lang": ["java"]}] | - Scenario: g_V_valueMapXtrueX + Scenario: g_V_valueMap_withXtokensX Given the modern graph And the traversal of """ - g.V().valueMap(true) + g.V().valueMap().with(WithOptions.tokens) """ When iterated to list Then the result should be unordered @@ -65,11 +65,11 @@ Feature: Step - valueMap() | m[{"name": ["lop"]}] | | m[{"name": ["ripple"]}] | - Scenario: g_V_valueMapXtrue_name_ageX + Scenario: g_V_valueMapXname_ageX_withXtokensX Given the modern graph And the traversal of """ - g.V().valueMap(true, "name", "age") + g.V().valueMap("name", "age").with(WithOptions.tokens) """ When iterated to list Then the result should be unordered @@ -81,6 +81,22 @@ Feature: Step - valueMap() | m[{"t[id]": "v[lop].id", "t[label]": "software", "name": ["lop"]}] | | m[{"t[id]": "v[ripple].id", "t[label]": "software", "name": ["ripple"]}] | + Scenario: g_V_valueMapXname_ageX_withXtokens_labelsX_byXunfoldX + Given the modern graph + And the traversal of + """ + g.V().valueMap("name", "age").with(WithOptions.tokens, WithOptions.labels).by(__.unfold()) + """ + When iterated to list + Then the result should be unordered + | result | + | m[{"t[label]": "person", "name": "marko", "age": 29}] | + | m[{"t[label]": "person", "name": "josh", "age": 32}] | + | m[{"t[label]": "person", "name": "peter", "age": 35}] | + | m[{"t[label]": "person", "name": "vadas", "age": 27}] | + | m[{"t[label]": "software", "name": "lop"}] | + | m[{"t[label]": "software", "name": "ripple"}] | + Scenario: g_VX1X_outXcreatedX_valueMap Given the modern graph And using the parameter v1Id defined as "v[marko].id" @@ -93,15 +109,28 @@ Feature: Step - valueMap() | result | | m[{"name": ["lop"], "lang": ["java"]}] | - Scenario: g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMapXtrueX + Scenario: g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX Given the modern graph And the traversal of """ - g.V().hasLabel("person").filter(__.outE("created")).valueMap(true) + g.V().hasLabel("person").filter(__.outE("created")).valueMap().with(WithOptions.tokens) """ When iterated to list Then the result should be unordered | result | | m[{"name": ["marko"], "age": [29], "t[label]":"person", "t[id]":"v[marko].id"}] | | m[{"name": ["josh"], "age": [32], "t[label]":"person", "t[id]":"v[josh].id"}] | - | m[{"name": ["peter"], "age": [35], "t[label]":"person", "t[id]":"v[peter].id"}] | \ No newline at end of file + | m[{"name": ["peter"], "age": [35], "t[label]":"person", "t[id]":"v[peter].id"}] | + + Scenario: g_VX1X_valueMapXname_locationX_byXunfoldX_by + Given the crew graph + And using the parameter v1Id defined as "v[marko].id" + And the traversal of + """ + g.V(v1Id).valueMap("name", "location").by(__.unfold()).by() + """ + When iterated to list + Then the result should be unordered + | result | + | m[{"name": "marko", "location": ["san diego", "santa cruz", "brussels", "santa fe"]}] | + diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java index 2972ee8..13b573f 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java @@ -2495,7 +2495,7 @@ public class GraphComputerTest extends AbstractGremlinProcessTest { try { g.V().repeat(__.out()).emit().program(vp).dedup() - .valueMap("name", "pl").forEachRemaining((Map<String, Object> map) -> { + .valueMap("name", "pl").forEachRemaining((Map<Object, Object> map) -> { final String name = (String) ((List) map.get("name")).get(0); final List<Integer> pathLengths = (List<Integer>) map.get("pl"); diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java index f732a44..21fbb8c 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith; import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; @@ -65,7 +66,7 @@ public abstract class AddVertexTest extends AbstractGremlinTest { public abstract Traversal<Vertex, Vertex> get_g_V_addVXanimalX_propertyXname_valuesXnameXX_propertyXname_an_animalX_propertyXvaluesXnameX_labelX(); - public abstract Traversal<Vertex, Map<String, List<String>>> get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX(); + public abstract Traversal<Vertex, Map<Object, List<String>>> get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX(); public abstract Traversal<Vertex, String> get_g_withSideEffectXa_markoX_addV_propertyXname_selectXaXX_name(); @@ -73,7 +74,7 @@ public abstract class AddVertexTest extends AbstractGremlinTest { public abstract Traversal<Vertex, String> get_g_withSideEffectXa_nameX_addV_propertyXselectXaX_markoX_name(); - public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMapXtrueX(); + public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX(); @Test @LoadGraphWith(MODERN) @@ -235,12 +236,12 @@ public abstract class AddVertexTest extends AbstractGremlinTest { @LoadGraphWith(MODERN) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) public void g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX() { - final Traversal<Vertex, Map<String, List<String>>> traversal = get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX(); + final Traversal<Vertex, Map<Object, List<String>>> traversal = get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX(); printTraversalForm(traversal); int counter = 0; while (traversal.hasNext()) { counter++; - final Map<String, List<String>> valueMap = traversal.next(); + final Map<Object, List<String>> valueMap = traversal.next(); assertEquals(2, valueMap.size()); assertEquals(Collections.singletonList("test"), valueMap.get("temp")); assertTrue(valueMap.get("name").equals(Collections.singletonList("ripple")) || valueMap.get("name").equals(Collections.singletonList("lop"))); @@ -285,8 +286,8 @@ public abstract class AddVertexTest extends AbstractGremlinTest { @LoadGraphWith(MODERN) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) - public void g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMapXtrueX() { - final Traversal<Vertex, Map<Object,Object>> traversal = get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMapXtrueX(); + public void g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX() { + final Traversal<Vertex, Map<Object,Object>> traversal = get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX(); printTraversalForm(traversal); final Map<Object,Object> map = traversal.next(); assertFalse(traversal.hasNext()); @@ -339,7 +340,7 @@ public abstract class AddVertexTest extends AbstractGremlinTest { } @Override - public Traversal<Vertex, Map<String, List<String>>> get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX() { + public Traversal<Vertex, Map<Object, List<String>>> get_g_withSideEffectXa_testX_V_hasLabelXsoftwareX_propertyXtemp_selectXaXX_valueMapXname_tempX() { return g.withSideEffect("a", "test").V().hasLabel("software").property("temp", select("a")).valueMap("name", "temp"); } @@ -359,8 +360,8 @@ public abstract class AddVertexTest extends AbstractGremlinTest { } @Override - public Traversal<Vertex, Map<Object, Object>> get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMapXtrueX() { - return g.V().as("a").has("name", "marko").out("created").as("b").addV(select("a").label()).property("test", select("b").label()).valueMap(true); + public Traversal<Vertex, Map<Object, Object>> get_g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX() { + return g.V().as("a").has("name", "marko").out("created").as("b").addV(select("a").label()).property("test", select("b").label()).valueMap().with(WithOptions.tokens); } } } \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java index 107831a..864ebb0 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java @@ -51,7 +51,7 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Vertex> get_g_V_pageRank_hasXpageRankX(); - public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX(); + public abstract Traversal<Vertex, Map<Object, List<Object>>> get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX(); public abstract Traversal<Vertex, String> get_g_V_pageRank_order_byXpageRank_descX_byXnameX_name(); @@ -63,7 +63,7 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Map<String, Object>> get_g_V_pageRank_byXpageRankX_asXaX_outXknowsX_pageRank_asXbX_selectXa_bX_by_byXmathX(); - public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX(); + public abstract Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX(); public abstract Traversal<Vertex, Map<Object, List<Vertex>>> get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_withXpropertyName_pageRankXX_withXedges_inEXX_withXtimes_1X_inXcreatedX_groupXmX_byXpageRankX_capXmX(); @@ -80,9 +80,9 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) public void g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_valueMapXname_projectRankX() { - final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX(); + final Traversal<Vertex, Map<Object, List<Object>>> traversal = get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX(); printTraversalForm(traversal); - final List<Map<String, List<Object>>> result = traversal.toList(); + final List<Map<Object, List<Object>>> result = traversal.toList(); assertEquals(4, result.size()); final Map<String, Double> map = new HashMap<>(); result.forEach(m -> map.put((String) m.get("name").get(0), (Double) m.get("projectRank").get(0))); @@ -184,11 +184,11 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) public void g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX() { - final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX(); + final Traversal<Vertex, Map<Object, List<Object>>> traversal = get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX(); printTraversalForm(traversal); int counter = 0; while (traversal.hasNext()) { - final Map<String, List<Object>> map = traversal.next(); + final Map<Object, List<Object>> map = traversal.next(); assertEquals(2, map.size()); String name = (String) map.get("name").get(0); double pageRank = (Double) map.get("priors").get(0); @@ -274,7 +274,7 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List<Object>>> get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX() { + public Traversal<Vertex, Map<Object, List<Object>>> get_g_V_outXcreatedX_pageRank_byXbothEX_byXprojectRankX_timesX0X_valueMapXname_projectRankX() { return g.V().out("created").pageRank().by(__.bothE()).by("projectRank").times(0).valueMap("name", "projectRank"); } @@ -304,7 +304,7 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX() { + public Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX() { return g.V().hasLabel("software").has("name", "ripple").pageRank(1.0).by(__.inE("created")).times(1).by("priors").in("created").union(__.both(), __.identity()).valueMap("name", "priors"); } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java index d53ec3e..5087522 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java @@ -53,9 +53,9 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Map<Object, Number>> get_g_V_peerPressure_byXclusterX_byXoutEXknowsXX_pageRankX1X_byXrankX_byXoutEXknowsXX_timesX2X_group_byXclusterX_byXrank_sumX_limitX100X(); - public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); + public abstract Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); - public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); + public abstract Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); @Test @LoadGraphWith(MODERN) @@ -83,9 +83,9 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest { @LoadGraphWith(MODERN) public void g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { TestHelper.assumeNonDeterministic(); - final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); + final Traversal<Vertex, Map<Object, List<Object>>> traversal = get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); printTraversalForm(traversal); - final List<Map<String, List<Object>>> results = traversal.toList(); + final List<Map<Object, List<Object>>> results = traversal.toList(); assertEquals(6, results.size()); final Map<String, Object> clusters = new HashMap<>(); results.forEach(m -> clusters.put((String) m.get("name").get(0), m.get("cluster").get(0))); @@ -105,9 +105,9 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest { @LoadGraphWith(MODERN) public void g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXEDGES_outEX_withXPROPERTY_NAME_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { TestHelper.assumeNonDeterministic(); - final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); + final Traversal<Vertex, Map<Object, List<Object>>> traversal = get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX(); printTraversalForm(traversal); - final List<Map<String, List<Object>>> results = traversal.toList(); + final List<Map<Object, List<Object>>> results = traversal.toList(); assertEquals(6, results.size()); final Map<String, Object> clusters = new HashMap<>(); results.forEach(m -> clusters.put((String) m.get("name").get(0), m.get("cluster").get(0))); @@ -136,12 +136,12 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { + public Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { return g.V().has("name", "ripple").in("created").peerPressure().by(__.outE()).by("cluster").repeat(__.union(__.identity(), __.both())).times(2).dedup().valueMap("name", "cluster"); } @Override - public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { + public Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXedges_outEX_withXpropertyName_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() { return g.V().has("name", "ripple").in("created").peerPressure().with(PeerPressure.edges,__.outE()).with(PeerPressure.propertyName, "cluster").repeat(__.union(__.identity(), __.both())).times(2).dedup().valueMap("name", "cluster"); } } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProgramTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProgramTest.java index 1923352..a0c5b23 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProgramTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProgramTest.java @@ -71,7 +71,7 @@ public abstract class ProgramTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Vertex> get_g_V_programXpageRankX(); - public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX(); + public abstract Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX(); public abstract Traversal<Vertex, Map<String, Object>> get_g_V_outXcreatedX_aggregateXxX_byXlangX_groupCount_programXTestProgramX_asXaX_selectXa_xX(); @@ -92,12 +92,12 @@ public abstract class ProgramTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) public void g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX() { - final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX(); + final Traversal<Vertex, Map<Object, List<Object>>> traversal = get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX(); printTraversalForm(traversal); int counter = 0; double lastRank = Double.MIN_VALUE; while (traversal.hasNext()) { - final Map<String, List<Object>> map = traversal.next(); + final Map<Object, List<Object>> map = traversal.next(); assertEquals(2, map.size()); assertEquals(1, map.get("name").size()); assertEquals(1, map.get("rank").size()); @@ -148,7 +148,7 @@ public abstract class ProgramTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX() { + public Traversal<Vertex, Map<Object, List<Object>>> get_g_V_hasLabelXpersonX_programXpageRank_rankX_order_byXrank_ascX_valueMapXname_rankX() { return g.V().hasLabel("person").program(PageRankVertexProgram.build().property("rank").create(graph)).order().by("rank", Order.asc).valueMap("name", "rank"); } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ValueMapTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ValueMapTest.java index 6962caf..219a6b0 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ValueMapTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ValueMapTest.java @@ -23,14 +23,17 @@ import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.List; import java.util.Map; +import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.CREW; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -49,27 +52,35 @@ import static org.junit.Assert.assertFalse; @RunWith(GremlinProcessRunner.class) public abstract class ValueMapTest extends AbstractGremlinProcessTest { - public abstract Traversal<Vertex, Map<String, List>> get_g_V_valueMap(); + public abstract Traversal<Vertex, Map<Object, List>> get_g_V_valueMap(); public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXtrueX(); - public abstract Traversal<Vertex, Map<String, List>> get_g_V_valueMapXname_ageX(); + public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_valueMap_withXtokensX(); - public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMapXtrueX(); + public abstract Traversal<Vertex, Map<Object, List>> get_g_V_valueMapXname_ageX(); + + public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX(); public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXtrue_name_ageX(); - public abstract Traversal<Vertex, Map<String, List<String>>> get_g_VX1X_outXcreatedX_valueMap(final Object v1Id); + public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXname_ageX_withXtokensX(); + + public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXname_ageX_withXtokens_labelsX_byXunfoldX(); + + public abstract Traversal<Vertex, Map<Object, List<String>>> get_g_VX1X_outXcreatedX_valueMap(final Object v1Id); + + public abstract Traversal<Vertex, Map<Object, Object>> get_g_VX1X_valueMapXname_locationX_byXunfoldX_by(final Object v1Id); @Test @LoadGraphWith(MODERN) public void g_V_valueMap() { - final Traversal<Vertex, Map<String, List>> traversal = get_g_V_valueMap(); + final Traversal<Vertex, Map<Object, List>> traversal = get_g_V_valueMap(); printTraversalForm(traversal); int counter = 0; while (traversal.hasNext()) { counter++; - final Map<String, List> values = traversal.next(); + final Map<Object, List> values = traversal.next(); final String name = (String) values.get("name").get(0); assertEquals(2, values.size()); if (name.equals("marko")) { @@ -93,50 +104,52 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) - public void g_V_valueMapXtrueX() { - final Traversal<Vertex, Map<Object, Object>> traversal = get_g_V_valueMapXtrueX(); - printTraversalForm(traversal); - int counter = 0; - while (traversal.hasNext()) { - counter++; - final Map<Object, Object> values = traversal.next(); - final String name = (String) ((List) values.get("name")).get(0); - assertEquals(4, values.size()); - assertThat(values.containsKey(T.id), is(true)); - if (name.equals("marko")) { - assertEquals(29, ((List) values.get("age")).get(0)); - assertEquals("person", values.get(T.label)); - } else if (name.equals("josh")) { - assertEquals(32, ((List) values.get("age")).get(0)); - assertEquals("person", values.get(T.label)); - } else if (name.equals("peter")) { - assertEquals(35, ((List) values.get("age")).get(0)); - assertEquals("person", values.get(T.label)); - } else if (name.equals("vadas")) { - assertEquals(27, ((List) values.get("age")).get(0)); - assertEquals("person", values.get(T.label)); - } else if (name.equals("lop")) { - assertEquals("java", ((List) values.get("lang")).get(0)); - assertEquals("software", values.get(T.label)); - } else if (name.equals("ripple")) { - assertEquals("java", ((List) values.get("lang")).get(0)); - assertEquals("software", values.get(T.label)); - } else { - throw new IllegalStateException("It is not possible to reach here: " + values); + public void g_V_valueMap_withXtokensX() { + for (final Traversal<Vertex, Map<Object, Object>> traversal : + Arrays.asList(get_g_V_valueMapXtrueX(), get_g_V_valueMap_withXtokensX())) { + printTraversalForm(traversal); + int counter = 0; + while (traversal.hasNext()) { + counter++; + final Map<Object, Object> values = traversal.next(); + final String name = (String) ((List) values.get("name")).get(0); + assertEquals(4, values.size()); + assertThat(values.containsKey(T.id), is(true)); + if (name.equals("marko")) { + assertEquals(29, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("josh")) { + assertEquals(32, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("peter")) { + assertEquals(35, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("vadas")) { + assertEquals(27, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("lop")) { + assertEquals("java", ((List) values.get("lang")).get(0)); + assertEquals("software", values.get(T.label)); + } else if (name.equals("ripple")) { + assertEquals("java", ((List) values.get("lang")).get(0)); + assertEquals("software", values.get(T.label)); + } else { + throw new IllegalStateException("It is not possible to reach here: " + values); + } } + assertEquals(6, counter); } - assertEquals(6, counter); } @Test @LoadGraphWith(MODERN) public void g_V_valueMapXname_ageX() { - final Traversal<Vertex, Map<String, List>> traversal = get_g_V_valueMapXname_ageX(); + final Traversal<Vertex, Map<Object, List>> traversal = get_g_V_valueMapXname_ageX(); printTraversalForm(traversal); int counter = 0; while (traversal.hasNext()) { counter++; - final Map<String, List> values = traversal.next(); + final Map<Object, List> values = traversal.next(); final String name = (String) values.get("name").get(0); if (name.equals("marko")) { assertEquals(29, values.get("age").get(0)); @@ -165,37 +178,81 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) - public void g_V_valueMapXtrue_name_ageX() { - final Traversal<Vertex, Map<Object, Object>> traversal = get_g_V_valueMapXtrue_name_ageX(); + public void g_V_valueMapXname_ageX_withXtokensX() { + for (final Traversal<Vertex, Map<Object, Object>> traversal : + Arrays.asList(get_g_V_valueMapXtrue_name_ageX(), get_g_V_valueMapXname_ageX_withXtokensX())) { + printTraversalForm(traversal); + int counter = 0; + while (traversal.hasNext()) { + counter++; + final Map<Object, Object> values = traversal.next(); + final String name = (String) ((List) values.get("name")).get(0); + assertThat(values.containsKey(T.id), is(true)); + if (name.equals("marko")) { + assertEquals(4, values.size()); + assertEquals(29, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("josh")) { + assertEquals(4, values.size()); + assertEquals(32, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("peter")) { + assertEquals(4, values.size()); + assertEquals(35, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("vadas")) { + assertEquals(4, values.size()); + assertEquals(27, ((List) values.get("age")).get(0)); + assertEquals("person", values.get(T.label)); + } else if (name.equals("lop")) { + assertEquals(3, values.size()); + assertNull(values.get("lang")); + assertEquals("software", values.get(T.label)); + } else if (name.equals("ripple")) { + assertEquals(3, values.size()); + assertNull(values.get("lang")); + assertEquals("software", values.get(T.label)); + } else { + throw new IllegalStateException("It is not possible to reach here: " + values); + } + } + assertEquals(6, counter); + } + } + + @Test + @LoadGraphWith(MODERN) + public void g_V_valueMapXname_ageX_withXtokens_labelsX_byXunfoldX() { + final Traversal<Vertex, Map<Object, Object>> traversal = get_g_V_valueMapXname_ageX_withXtokens_labelsX_byXunfoldX(); printTraversalForm(traversal); int counter = 0; while (traversal.hasNext()) { counter++; final Map<Object, Object> values = traversal.next(); - final String name = (String) ((List) values.get("name")).get(0); - assertThat(values.containsKey(T.id), is(true)); + final String name = (String) values.get("name"); + assertThat(values.containsKey(T.id), is(false)); if (name.equals("marko")) { - assertEquals(4, values.size()); - assertEquals(29, ((List) values.get("age")).get(0)); + assertEquals(3, values.size()); + assertEquals(29, values.get("age")); assertEquals("person", values.get(T.label)); } else if (name.equals("josh")) { - assertEquals(4, values.size()); - assertEquals(32, ((List) values.get("age")).get(0)); + assertEquals(3, values.size()); + assertEquals(32, values.get("age")); assertEquals("person", values.get(T.label)); } else if (name.equals("peter")) { - assertEquals(4, values.size()); - assertEquals(35, ((List) values.get("age")).get(0)); + assertEquals(3, values.size()); + assertEquals(35, values.get("age")); assertEquals("person", values.get(T.label)); } else if (name.equals("vadas")) { - assertEquals(4, values.size()); - assertEquals(27, ((List) values.get("age")).get(0)); + assertEquals(3, values.size()); + assertEquals(27, values.get("age")); assertEquals("person", values.get(T.label)); } else if (name.equals("lop")) { - assertEquals(3, values.size()); + assertEquals(2, values.size()); assertNull(values.get("lang")); assertEquals("software", values.get(T.label)); } else if (name.equals("ripple")) { - assertEquals(3, values.size()); + assertEquals(2, values.size()); assertNull(values.get("lang")); assertEquals("software", values.get(T.label)); } else { @@ -208,15 +265,14 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) public void g_VX1X_outXcreatedX_valueMap() { - final Traversal<Vertex, Map<String, List<String>>> traversal = get_g_VX1X_outXcreatedX_valueMap(convertToVertexId("marko")); + final Traversal<Vertex, Map<Object, List<String>>> traversal = get_g_VX1X_outXcreatedX_valueMap(convertToVertexId("marko")); printTraversalForm(traversal); assertTrue(traversal.hasNext()); - final Map<String, List<String>> values = traversal.next(); + final Map<Object, List<String>> values = traversal.next(); assertFalse(traversal.hasNext()); assertEquals("lop", values.get("name").get(0)); assertEquals("java", values.get("lang").get(0)); assertEquals(2, values.size()); - } /** @@ -224,8 +280,8 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { */ @Test @LoadGraphWith(MODERN) - public void g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMapXtrueX() { - final Traversal<Vertex,Map<Object,Object>> gt=get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMapXtrueX(); + public void g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX() { + final Traversal<Vertex,Map<Object,Object>> gt = get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX(); int cnt = 0; while(gt.hasNext()){ final Map<Object,Object> m = gt.next(); @@ -242,9 +298,27 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { assertTrue(cnt > 0); } + @Test + @LoadGraphWith(CREW) + public void g_VX1X_valueMapXname_locationX_byXunfoldX_by() { + final Traversal<Vertex,Map<Object,Object>> traversal = get_g_VX1X_valueMapXname_locationX_byXunfoldX_by(convertToVertexId("marko")); + printTraversalForm(traversal); + assertTrue(traversal.hasNext()); + final Map<Object, Object> values = traversal.next(); + assertFalse(traversal.hasNext()); + assertTrue("name value should be a String", values.containsKey("name") && String.class.equals(values.get("name").getClass())); + assertTrue("location value should be a List", values.containsKey("location") && List.class.isAssignableFrom(values.get("location").getClass())); + assertEquals("marko", values.get("name")); + final List locations = (List) values.get("location"); + for (final String location : Arrays.asList("san diego", "santa cruz", "brussels", "santa fe")) { + assertTrue(locations.contains(location)); + } + assertEquals(2, values.size()); + } + public static class Traversals extends ValueMapTest { @Override - public Traversal<Vertex, Map<String, List>> get_g_V_valueMap() { + public Traversal<Vertex, Map<Object, List>> get_g_V_valueMap() { return g.V().valueMap(); } @@ -254,7 +328,12 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List>> get_g_V_valueMapXname_ageX() { + public Traversal<Vertex, Map<Object, Object>> get_g_V_valueMap_withXtokensX() { + return g.V().valueMap().with(WithOptions.tokens); + } + + @Override + public Traversal<Vertex, Map<Object, List>> get_g_V_valueMapXname_ageX() { return g.V().valueMap("name", "age"); } @@ -264,13 +343,29 @@ public abstract class ValueMapTest extends AbstractGremlinProcessTest { } @Override - public Traversal<Vertex, Map<String, List<String>>> get_g_VX1X_outXcreatedX_valueMap(final Object v1Id) { + public Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXname_ageX_withXtokensX() { + return g.V().valueMap("name", "age").with(WithOptions.tokens); + } + + @Override + public Traversal<Vertex, Map<Object, Object>> get_g_V_valueMapXname_ageX_withXtokens_labelsX_byXunfoldX() { + return g.V().valueMap("name", "age").with(WithOptions.tokens, WithOptions.labels).by(__.unfold()); + } + + @Override + public Traversal<Vertex, Map<Object, List<String>>> get_g_VX1X_outXcreatedX_valueMap(final Object v1Id) { return g.V(v1Id).out("created").valueMap(); } @Override - public Traversal<Vertex, Map<Object, Object>> get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMapXtrueX() { - return g.V().hasLabel("person").filter(__.outE("created")).valueMap(true); + public Traversal<Vertex, Map<Object, Object>> get_g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX() { + return g.V().hasLabel("person").filter(__.outE("created")).valueMap().with(WithOptions.tokens); } + + @Override + public Traversal<Vertex, Map<Object, Object>> get_g_VX1X_valueMapXname_locationX_byXunfoldX_by(final Object v1Id) { + return g.V(v1Id).valueMap("name","location").by(__.unfold()).by(); + } + } } diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java index a590835..a2019ae 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java @@ -19,18 +19,14 @@ package org.apache.tinkerpop.gremlin.tinkergraph.structure; import org.apache.tinkerpop.gremlin.process.computer.Computer; -import org.apache.tinkerpop.gremlin.process.traversal.IO; -import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy; import org.apache.tinkerpop.gremlin.structure.*; -import org.apache.tinkerpop.gremlin.structure.io.IoTest; import org.apache.tinkerpop.gremlin.structure.io.graphml.GraphMLIo; -import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; -import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoReader; import org.apache.tinkerpop.gremlin.util.TimeUtil; import org.junit.Ignore; import org.junit.Test; @@ -38,15 +34,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.BiFunction; import java.util.function.Supplier; import static org.apache.tinkerpop.gremlin.process.traversal.Operator.sum; import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.*; +import static org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions.*; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -123,92 +118,33 @@ public class TinkerGraphPlayTest { @Test @Ignore public void testPlayDK() throws Exception { + TinkerGraph graph = TinkerFactory.createTheCrew(); + GraphTraversalSource g = graph.traversal().withStrategies( + SubgraphStrategy.build().vertexProperties(hasNot("endTime")).create()); - final Map<String, String> aliases = new HashMap<>(); - aliases.put("marko","okram"); - final GraphTraversalSource g = TinkerFactory.createModern().traversal(); - /*g.withSideEffect("a", aliases).V().hasLabel("person"). - values("name").as("n"). - optional(select("a").select(select("n"))). - forEachRemaining(System.out::println);*/ - - // shortest path lengths (by summed weight) - g.withSack(0.0).V().has("person", "name", "marko"). - repeat(__.bothE(). - sack(sum). - by("weight"). - otherV(). - group("m"). - by(). - by(sack().min()).as("x"). - // where(P.eq("m")).by(sack()).by(select(select("x"))). // could be that easy, but "x" is unknown here - filter(project("s","x"). - by(sack()). - by(select("m").select(select("x"))). - where("s", P.eq("x"))). - group("p"). - by(). - by(project("path","length"). - by(path().by("name").by("weight")). - by(sack())) - ). - cap("p").unfold(). - group(). - by(select(Column.keys).values("name")). - by(Column.values).next().entrySet(). - forEach(System.out::println); - - System.out.println("---"); - - // longest path lengths (by summed weight) - g.withSack(0.0).V().has("person", "name", "marko"). - repeat(__.bothE().simplePath(). - sack(sum). - by("weight"). - otherV(). - group("m"). - by(). - by(sack().max()).as("x"). - filter(project("s","x"). - by(sack()). - by(select("m").select(select("x"))). - where("s", P.eq("x"))). - group("p"). - by(). - by(project("path","length"). - by(path().by("name").by("weight")). - by(sack())) - ). - cap("p").unfold(). - group(). - by(select(Column.keys).values("name")). - by(Column.values).next().entrySet(). - forEach(System.out::println); - - System.out.println("---"); - - // all shortest paths (by summed weight) - g.withSack(0.0).V().as("a"). - repeat(__.bothE(). - sack(sum). - by("weight"). - otherV().as("b"). - group("m"). - by(select("a","b").by("name")). - by(sack().min()). - filter(project("s","x"). - by(sack()). - by(select("m").select(select("a", "b").by("name"))). - where("s", P.eq("x"))). - group("p"). - by(select("a","b").by("name")). - by(map(union(path().by("name").by("weight"), sack()).fold())) - ). - cap("p").unfold(). - order(). - by(select(Column.keys).select("a")). - by(select(Column.keys).select("b")). - forEachRemaining(System.out::println); + System.out.println("\n--- valueMap().by(unfold()) ---"); + g.V().valueMap().forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()) ---"); + g.V().valueMap().by(unfold()).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens) ---"); + g.V().valueMap().by(unfold()).with(tokens).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens, false) ---"); + g.V().valueMap().by(unfold()).with(tokens, false).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens, ids) ---"); + g.V().valueMap().by(unfold()).with(tokens, ids).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens, labels) ---"); + g.V().valueMap().by(unfold()).with(tokens, labels).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens, all) ---"); + g.V().valueMap().by(unfold()).with(tokens, all).forEachRemaining(System.out::println); + + System.out.println("\n--- valueMap().by(unfold()).with(tokens, ids | labels) ---"); + g.V().valueMap().by(unfold()).with(tokens, ids | labels).forEachRemaining(System.out::println); } @Test