This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master by this push: new a0cf823bf6 Implement asString(), length(), toLower(), and toUpper() String Functions (#2205) a0cf823bf6 is described below commit a0cf823bf663d80b733ab56d57a22f215517f42f Author: Yang Xia <55853655+xia...@users.noreply.github.com> AuthorDate: Mon Sep 18 12:37:59 2023 -0700 Implement asString(), length(), toLower(), and toUpper() String Functions (#2205) * add asString(), length(), toLower(), and toUpper() string functions * clean up & update length() to return int --- CHANGELOG.asciidoc | 2 + docs/src/dev/developer/for-committers.asciidoc | 5 +- docs/src/dev/provider/gremlin-semantics.asciidoc | 102 +++++++++++++- docs/src/reference/the-traversal.asciidoc | 68 ++++++++++ docs/src/upgrade/release-3.7.x.asciidoc | 62 +++++++++ .../grammar/DefaultGremlinBaseVisitor.java | 16 +++ .../language/grammar/TraversalMethodVisitor.java | 32 +++++ .../traversal/dsl/graph/GraphTraversal.java | 59 +++++++++ .../gremlin/process/traversal/dsl/graph/__.java | 28 ++++ .../process/traversal/step/map/AsStringStep.java | 51 +++++++ .../process/traversal/step/map/LengthStep.java | 60 +++++++++ .../process/traversal/step/map/ToLowerStep.java | 60 +++++++++ .../process/traversal/step/map/ToUpperStep.java | 60 +++++++++ .../traversal/translator/PythonTranslator.java | 3 + .../process/traversal/util/BytecodeHelper.java | 8 ++ .../grammar/TraversalMethodVisitorTest.java | 20 +++ .../traversal/step/map/AsStringStepTest.java | 62 +++++++++ .../process/traversal/step/map/LengthStepTest.java | 56 ++++++++ .../traversal/step/map/ToLowerStepTest.java | 55 ++++++++ .../traversal/step/map/ToUpperStepTest.java | 55 ++++++++ .../Process/Traversal/GraphTraversal.cs | 36 +++++ .../src/Gremlin.Net/Process/Traversal/__.cs | 32 +++++ .../Gherkin/CommonSteps.cs | 1 + .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 18 +++ gremlin-go/driver/anonymousTraversal.go | 28 ++++ gremlin-go/driver/cucumber/cucumberSteps_test.go | 1 + gremlin-go/driver/cucumber/gremlin.go | 18 +++ gremlin-go/driver/graph.go | 8 +- gremlin-go/driver/graphTraversal.go | 24 ++++ .../lib/process/graph-traversal.js | 44 +++++++ .../test/cucumber/feature-steps.js | 1 + .../gremlin-javascript/test/cucumber/gremlin.js | 18 +++ gremlin-language/src/main/antlr4/Gremlin.g4 | 21 +++ .../gremlin_python/process/graph_traversal.py | 58 +++++++- .../src/main/python/radish/feature_steps.py | 2 + gremlin-python/src/main/python/radish/gremlin.py | 18 +++ .../tinkerpop/gremlin/features/StepDefinition.java | 3 + .../gremlin/test/features/map/AsString.feature | 146 +++++++++++++++++++++ .../gremlin/test/features/map/Length.feature | 60 +++++++++ .../gremlin/test/features/map/ToLower.feature | 75 +++++++++++ .../gremlin/test/features/map/ToUpper.feature | 74 +++++++++++ 41 files changed, 1543 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 797dd7262b..e917de0d91 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -25,6 +25,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima This release also includes changes from <<release-3-6-5, 3.6.6>> and <<release-3-5-8, 3.5.8>>. +* Added the `asString()`, `length()`, `toLower()`, and `toUpper()` steps to perform `String` manipulations. +* Added Gherkin parsing support for specific string results using `str[]`. [[release-3-7.0]] diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc index 2785894eaa..c5a90d785e 100644 --- a/docs/src/dev/developer/for-committers.asciidoc +++ b/docs/src/dev/developer/for-committers.asciidoc @@ -425,7 +425,10 @@ edges, maps, and any other available type. * Set - *s[_xxx_,_yyy_,_zzz_,...]* - A comma separated collection of values that make up the set should be added to between the square brackets. These values respect the type system thus allowing for creation of sets of vertices, edges, maps, and any other available type. -* String - Any value not using the system notation will be interpreted as a string. +* String - Any value not using the system notation will be interpreted as +a string by default. +** *str[_xxx_]* (Optional) - xxx should be replaced with a string. Optional notation used for specific string results, +such as null and spaces. * T - *t[_xxx_]* - The "xxx" should be replaced with a value of the `T` enum, such as `id` or `label`. * Vertex - *v[_xxx_]* - The "xxx" should be replaced with the "name" property of a vertex in the graph. This syntax may include the `.id` suffix which would indicate getting the vertex identifier or the `.sid` suffix which gets a string diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index b35b0aa16d..c14604bf9b 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -524,6 +524,31 @@ fully demonstrative of Gremlin step semantics. It is also hard to simply read th step is meant to behave. This section discusses the semantics for individual steps to help users and providers understand implementation expectations. +[[asString-step]] +=== asString() + +*Description:* Returns the value of incoming traverser as strings + +*Syntax:* `asString()` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`any` |`String` +|========================================================= + +*Arguments:* + +None + +Null values from the incoming traverser are not processed and remain as null when returned. + +*Exceptions* +* If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asString-step[reference] + [[call-step]] === call() @@ -723,6 +748,31 @@ None None +[[length-step]] +=== length() + +*Description:* Returns the length incoming string traverser. + +*Syntax:* `length()` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`String` |`Integer` +|========================================================= + +*Arguments:* + +None + +Null values from the incoming traverser are not processed and remain as null when returned. + +*Exceptions* +* If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#length-step[reference] + [[merge-e-step]] === mergeE() @@ -863,4 +913,54 @@ resolve to a `Map`. * As is common to Gremlin, it is expected that `Traversal` arguments may utilize `sideEffect()` steps. See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java[source], -link:https://tinkerpop.apache.org/docs/x.y.z/reference/#mergev-step[reference] \ No newline at end of file +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#mergev-step[reference] + +[[toLower-step]] +=== toLower() + +*Description:* Returns the lowercase representation of incoming string traverser. + +*Syntax:* `toLower()` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`String` |`String` +|========================================================= + +*Arguments:* + +None + +Null values from the incoming traverser are not processed and remain as null when returned. + +*Exceptions* +* If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toLower-step[reference] + +[[toUpper-step]] +=== toUpper() + +*Description:* Returns the uppercase representation of incoming string traverser. + +*Syntax:* `toUpper()` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`String` |`String` +|========================================================= + +*Arguments:* + +None + +Null values from the incoming traverser are not processed and remain as null when returned. + +*Exceptions* +* If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toUpper-step[reference] \ No newline at end of file diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index 62c52ab0c3..cdb5631771 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -731,6 +731,24 @@ g.V().hasLabel('software').as('a','b','c'). link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#as-java.lang.String-java.lang.String...-++[`as(String,String...)`] +[[asString-step]] +=== AsString Step + +The `asString()`-step (*map*) returns the value of incoming traverser as strings. Null values are returned as a string value "null". + +[gremlin-groovy,modern] +---- +g.V().hasLabel('person').values('age').asString() <1> +g.V().hasLabel('person').values('age').asString().concat(' years old') <2> +---- + +<1> Return ages as string. +<2> Return ages as string and use concat to generate phrases. + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asString--++[`asString()`] + [[barrier-step]] === Barrier Step @@ -1991,6 +2009,23 @@ g.V(1).properties().label() link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#label--++[`label()`] +[[length-step]] +=== Length Step + +The `length()`-step (*map*) returns the length incoming string traverser. Null values are not processed and remain as null when returned. +If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.V().values('name').length() <1> +---- + +<1> Return the string length of all vertex names. + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#length--++[`length()`] + [[limit-step]] === Limit Step @@ -4243,6 +4278,39 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toV-org.apache.tinkerpop.gremlin.structure.Direction-++[`toV(Direction)`], link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/structure/Direction.html++[`Direction`] +[[toLower-step]] +=== ToLower Step + +The `toLower()`-step (*map*) returns the lowercase representation of incoming string traverser. Null values are not processed and remain as null when returned. +If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.inject("HELLO", "wORlD", null).toLower() +---- + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toLower--++[`toLower()`] + +[[toUpper-step]] +=== ToUpper Step + +The `toUpper()`-step (*map*) returns the uppercase representation of incoming string traverser. Null values are not processed and remain as null when returned. +If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.inject("hello", "wORlD", null).toUpper() +g.V().values("name").toUpper() <1> +---- + +<1> Returns the upper case representation of all vertex names. + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toUpper--++[`toUpper()`] + [[tree-step]] === Tree Step diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc index b217f59be6..f8c57b7ed2 100644 --- a/docs/src/upgrade/release-3.7.x.asciidoc +++ b/docs/src/upgrade/release-3.7.x.asciidoc @@ -29,7 +29,69 @@ complete list of all the modifications that are part of this release. === Upgrading for Users +==== String Manipulation Steps +Additional String manipulation steps have been added to this version: `asString()`, `length()`, `toLower()`, `toUpper()`. +The following example demonstrates the use of a closure to perform the above functions: +[source,text] +---- +gremlin> g.V().hasLabel("person").values("age").map{it.get().toString()} +==>29 +==>27 +==>32 +==>35 +gremlin> g.V().values("name").map{it.get().length()} +==>5 +==>5 +==>3 +==>4 +==>6 +==>5 +gremlin> g.inject("TO", "LoWeR", "cAsE").map{it.get().toLowerCase()} +==>to +==>lower +==>case +gremlin> g.V().values("name").map{it.get().toUpperCase()} +==>MARKO +==>VADAS +==>LOP +==>JOSH +==>RIPPLE +==>PETER +---- +With these additional steps this operation can be performed with standard Gremlin syntax: +[source,text] +---- +gremlin> g.V().hasLabel("person").values("age").asString() +==>29 +==>27 +==>32 +==>35 +gremlin> g.V().values("name").length() +==>5 +==>5 +==>3 +==>4 +==>6 +==>5 +gremlin> g.inject("TO", "LoWeR", "cAsE").toLower() +==>to +==>lower +==>case +gremlin> g.V().values("name").toUpper() +==>MARKO +==>VADAS +==>LOP +==>JOSH +==>RIPPLE +==>PETER +---- + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-2672[TINKERPOP-2672], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asString-step[asString()-step], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#length-step[length()-step], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toLower-step[toLower()-step], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toUpper-step[toUpper()-step] === Upgrading for Providers diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java index ee160101c1..fbc432253b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java @@ -921,6 +921,22 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im */ @Override public T visitTraversalMethod_concat_String(final GremlinParser.TraversalMethod_concat_StringContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_asString_Empty(final GremlinParser.TraversalMethod_asString_EmptyContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_toUpper_Empty(final GremlinParser.TraversalMethod_toUpper_EmptyContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_toLower_Empty(final GremlinParser.TraversalMethod_toLower_EmptyContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_length_Empty(final GremlinParser.TraversalMethod_length_EmptyContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index 68bf88ddb2..1208361955 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -1741,6 +1741,38 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> return graphTraversal.concat(antlr.genericVisitor.parseStringVarargs(ctx.stringLiteralVarargs())); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_asString_Empty(final GremlinParser.TraversalMethod_asString_EmptyContext ctx) { + return graphTraversal.asString(); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_toUpper_Empty(final GremlinParser.TraversalMethod_toUpper_EmptyContext ctx) { + return graphTraversal.toUpper(); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_toLower_Empty(final GremlinParser.TraversalMethod_toLower_EmptyContext ctx) { + return graphTraversal.toLower(); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_length_Empty(final GremlinParser.TraversalMethod_length_EmptyContext ctx) { + return graphTraversal.length(); + } + public GraphTraversal[] getNestedTraversalList(final GremlinParser.NestedTraversalListContext ctx) { return ctx.nestedTraversalExpr().nestedTraversal() .stream() 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 aa1d2fabb7..dceb08bcc7 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 @@ -79,6 +79,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConcatStep; @@ -100,6 +101,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.LabelStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaCollectingBarrierStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaFlatMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.LengthStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MathStep; @@ -128,6 +130,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TailLocalStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToLowerStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToUpperStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalFlatMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalSelectStep; @@ -1437,6 +1441,57 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { return this.asAdmin().addStep(new ConcatStep<>(this.asAdmin(), concatStrings)); } + /** + * Returns the value of incoming traverser as strings. Null values are returned as a string value "null". + * + * @return the traversal with an appended {@link AsStringStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#asString-step" target="_blank">Reference Documentation - AsString Step</a> + * @since 3.7.1 + */ + public default GraphTraversal<S, String> asString() { + this.asAdmin().getBytecode().addStep(Symbols.asString); + return this.asAdmin().addStep(new AsStringStep<>(this.asAdmin())); + } + + /** + * Returns the length incoming string traverser. Null values are not processed and remain as null when returned. + * If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. + * + * @return the traversal with an appended {@link LengthStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#length-step" target="_blank">Reference Documentation - Length Step</a> + * @since 3.7.1 + */ + public default GraphTraversal<S, Integer> length() { + this.asAdmin().getBytecode().addStep(Symbols.length); + return this.asAdmin().addStep(new LengthStep<>(this.asAdmin())); + } + + /** + * Returns the lowercase representation of incoming string traverser. Null values are not processed and remain + * as null when returned. If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. + * + * @return the traversal with an appended {@link ToLowerStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#toLower-step" target="_blank">Reference Documentation - ToLower Step</a> + * @since 3.7.1 + */ + public default GraphTraversal<S, String> toLower() { + this.asAdmin().getBytecode().addStep(Symbols.toLower); + return this.asAdmin().addStep(new ToLowerStep<>(this.asAdmin())); + } + + /** + * Returns the uppercase representation of incoming string traverser. Null values are not processed and + * remain as null when returned. If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. + * + * @return the traversal with an appended {@link ToUpperStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#toUpper-step" target="_blank">Reference Documentation - ToUpper Step</a> + * @since 3.7.1 + */ + public default GraphTraversal<S, String> toUpper() { + this.asAdmin().getBytecode().addStep(Symbols.toUpper); + return this.asAdmin().addStep(new ToUpperStep<>(this.asAdmin())); + } + ///////////////////// FILTER STEPS ///////////////////// /** @@ -3500,6 +3555,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { public static final String call = "call"; public static final String element = "element"; public static final String concat = "concat"; + public static final String asString = "asString"; + public static final String toUpper = "toUpper"; + public static final String toLower = "toLower"; + public static final String length = "length"; public static final String timeLimit = "timeLimit"; public static final String simplePath = "simplePath"; 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 ab3aa9fa95..dac4cb2a58 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 @@ -601,6 +601,34 @@ public class __ { return __.<A>start().concat(concatString); } + /** + * @see GraphTraversal#asString() + */ + public static <A> GraphTraversal<A, String> asString() { + return __.<A>start().asString(); + } + + /** + * @see GraphTraversal#length() + */ + public static <A> GraphTraversal<A, Integer> length() { + return __.<A>start().length(); + } + + /** + * @see GraphTraversal#toLower() + */ + public static <A> GraphTraversal<A, String> toLower() { + return __.<A>start().toLower(); + } + + /** + * @see GraphTraversal#toUpper() + */ + public static <A> GraphTraversal<A, String> toUpper() { + return __.<A>start().toUpper(); + } + ///////////////////// FILTER STEPS ///////////////////// /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStep.java new file mode 100644 index 0000000000..57b8cd4d27 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStep.java @@ -0,0 +1,51 @@ +/* + * 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.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; + +import java.util.Collections; +import java.util.Set; + +/** + * Reference implementation for asString() step, a mid-traversal step which returns the incoming traverser value + * as a string. Null values are returned as a string value "null". + * + * @author David Bechberger (http://bechberger.com) + * @author Yang Xia (http://github.com/xiazcy) + */ +public final class AsStringStep<S> extends ScalarMapStep<S, String> { + + public AsStringStep(final Traversal.Admin traversal) { + super(traversal); + } + + @Override + protected String map(final Traverser.Admin<S> traverser) { + return String.valueOf(traverser.get()); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return Collections.singleton(TraverserRequirement.OBJECT); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStep.java new file mode 100644 index 0000000000..5f7008b29e --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStep.java @@ -0,0 +1,60 @@ +/* + * 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.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; + +import java.util.Collections; +import java.util.Set; + +/** + * Reference implementation for length() step, a mid-traversal step which returns the length of the incoming string + * traverser. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String + * value then an {@code IllegalArgumentException} will be thrown. + * + * @author David Bechberger (http://bechberger.com) + * @author Yang Xia (http://github.com/xiazcy) + */ +public final class LengthStep<S> extends ScalarMapStep<S, Integer> { + + public LengthStep(final Traversal.Admin traversal) { + super(traversal); + } + + @Override + protected Integer map(final Traverser.Admin<S> traverser) { + final S item = traverser.get(); + // throws when incoming traverser isn't a string + if (null != item && !(item instanceof String)) { + throw new IllegalArgumentException( + String.format("The length() step can only take string as argument, encountered %s", item.getClass())); + } + + // we will pass null values to next step + return null == item? null : ((String) item).length(); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return Collections.singleton(TraverserRequirement.OBJECT); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStep.java new file mode 100644 index 0000000000..84cc5dd184 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStep.java @@ -0,0 +1,60 @@ +/* + * 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.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; + +import java.util.Collections; +import java.util.Set; + +/** + * Reference implementation for toLower() step, a mid-traversal step which returns a lower case string representation + * of the incoming string traverser. Null values are not processed and remain as null when returned. + * If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. + * + * @author David Bechberger (http://bechberger.com) + * @author Yang Xia (http://github.com/xiazcy) + */ +public final class ToLowerStep<S> extends ScalarMapStep<S, String> { + + public ToLowerStep(final Traversal.Admin traversal) { + super(traversal); + } + + @Override + protected String map(final Traverser.Admin<S> traverser) { + final S item = traverser.get(); + // throws when incoming traverser isn't a string + if (null != item && !(item instanceof String)) { + throw new IllegalArgumentException( + String.format("The toLower() step can only take string as argument, encountered %s", item.getClass())); + } + + // we will pass null values to next step + return null == item? null : ((String) item).toLowerCase(); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return Collections.singleton(TraverserRequirement.OBJECT); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStep.java new file mode 100644 index 0000000000..0858153182 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStep.java @@ -0,0 +1,60 @@ +/* + * 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.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; + +import java.util.Collections; +import java.util.Set; + +/** + * Reference implementation for toUpper() step, a mid-traversal step which returns an upper case string representation + * of the incoming string traverser. Null values are not processed and remain as null when returned. + * If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. + * + * @author David Bechberger (http://bechberger.com) + * @author Yang Xia (http://github.com/xiazcy) + */ +public final class ToUpperStep<S> extends ScalarMapStep<S, String> { + + public ToUpperStep(final Traversal.Admin traversal) { + super(traversal); + } + + @Override + protected String map(final Traverser.Admin<S> traverser) { + final S item = traverser.get(); + // throws when incoming traverser isn't a string + if (null != item && !(item instanceof String)) { + throw new IllegalArgumentException( + String.format("The toUpper() step can only take string as argument, encountered %s", item.getClass())); + } + + // we will pass null values to next step + return null == item? null : ((String) item).toUpperCase(); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return Collections.singleton(TraverserRequirement.OBJECT); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java index e26b02f8ad..aa325b36d9 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java @@ -430,6 +430,7 @@ public final class PythonTranslator implements Translator.ScriptTranslator { TO_PYTHON_MAP.put("all", "all_"); TO_PYTHON_MAP.put("and", "and_"); TO_PYTHON_MAP.put("as", "as_"); + TO_PYTHON_MAP.put("asString", "as_string"); TO_PYTHON_MAP.put("filter", "filter_"); TO_PYTHON_MAP.put("from", "from_"); TO_PYTHON_MAP.put("id", "id_"); @@ -449,6 +450,8 @@ public final class PythonTranslator implements Translator.ScriptTranslator { TO_PYTHON_MAP.put("range", "range_"); TO_PYTHON_MAP.put("set", "set_"); TO_PYTHON_MAP.put("sum", "sum_"); + TO_PYTHON_MAP.put("toLower", "to_lower"); + TO_PYTHON_MAP.put("toUpper", "to_upper"); TO_PYTHON_MAP.put("with", "with_"); // TO_PYTHON_MAP.forEach((k, v) -> FROM_PYTHON_MAP.put(v, k)); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/BytecodeHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/BytecodeHelper.java index 85c7191642..648d23557e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/BytecodeHelper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/BytecodeHelper.java @@ -62,6 +62,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WherePredicate import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConcatStep; @@ -82,6 +83,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.LabelStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaCollectingBarrierStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaFlatMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.LengthStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MathStep; @@ -109,6 +111,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TailLocalStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToLowerStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ToUpperStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalFlatMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.TraversalSelectStep; @@ -198,6 +202,10 @@ public final class BytecodeHelper { put(GraphTraversal.Symbols.min, Arrays.asList(MinGlobalStep.class, MinGlobalStep.class)); put(GraphTraversal.Symbols.mean, Arrays.asList(MeanGlobalStep.class, MeanLocalStep.class)); put(GraphTraversal.Symbols.concat, Collections.singletonList(ConcatStep.class)); + put(GraphTraversal.Symbols.asString, Collections.singletonList(AsStringStep.class)); + put(GraphTraversal.Symbols.length, Collections.singletonList(LengthStep.class)); + put(GraphTraversal.Symbols.toLower, Collections.singletonList(ToLowerStep.class)); + put(GraphTraversal.Symbols.toUpper, Collections.singletonList(ToUpperStep.class)); put(GraphTraversal.Symbols.group, Arrays.asList(GroupStep.class, GroupSideEffectStep.class)); put(GraphTraversal.Symbols.groupCount, Arrays.asList(GroupCountStep.class, GroupCountSideEffectStep.class)); put(GraphTraversal.Symbols.tree, Arrays.asList(TreeStep.class, TreeSideEffectStep.class)); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java index da2833ae91..657dcc9d8a 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java @@ -1143,4 +1143,24 @@ public class TraversalMethodVisitorTest { compare(g.V().coalesce(E(1,2),addE("person")), eval("g.V().coalesce(__.E(1,2),__.addE('person'))")); } + + @Test + public void shouldParseTraversalMethod_asString_Empty() throws Exception { + compare(g.V().asString(), eval("g.V().asString()")); + } + + @Test + public void shouldParseTraversalMethod_toLower_Empty() throws Exception { + compare(g.V().toLower(), eval("g.V().toLower()")); + } + + @Test + public void shouldParseTraversalMethod_toUpper_Empty() throws Exception { + compare(g.V().toUpper(), eval("g.V().toUpper()")); + } + + @Test + public void shouldParseTraversalMethod_length_Empty() throws Exception { + compare(g.V().length(), eval("g.V().length()")); + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStepTest.java new file mode 100644 index 0000000000..a5391fc185 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringStepTest.java @@ -0,0 +1,62 @@ +/* + * 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.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.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * @author Yang Xia (http://github.com/xiazcy) + */ +public class AsStringStepTest extends StepTest { + + @Override + protected List<Traversal> getTraversals() { + return Collections.singletonList(__.asString()); + } + + @Test + public void testReturnTypes() { + assertEquals("1", __.__(1).asString().next()); + assertEquals("[]", __.__(Collections.emptyList()).asString().next()); + assertEquals("[1, 2]", __.__(Arrays.asList(1, 2)).asString().next()); + assertEquals("1", __.__(Arrays.asList(1, 2)).unfold().asString().next()); + assertArrayEquals(new String[]{"1", "2"}, __.inject(Arrays.asList(1, 2)).unfold().asString().toList().toArray()); + + assertEquals("null", __.__(null).asString().next()); + + assertEquals("[1, 2]test", __.__(Arrays.asList(1, 2)).asString().concat("test").next()); + assertEquals("1test", __.__(Arrays.asList(1, 2)).unfold().asString().concat("test").next()); + assertArrayEquals(new String[]{"1test", "2test"}, + __.__(Arrays.asList(1, 2)).unfold().asString().concat("test").toList().toArray()); + assertArrayEquals(new String[]{"1test", "2test"}, + __.__(Arrays.asList(1, 2)).unfold().asString().concat("test").fold().next().toArray()); + } + +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStepTest.java new file mode 100644 index 0000000000..22dfef9e90 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthStepTest.java @@ -0,0 +1,56 @@ +/* + * 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.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.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * @author Yang Xia (http://github.com/xiazcy) + */ +public class LengthStepTest extends StepTest { + + @Override + protected List<Traversal> getTraversals() { + return Collections.singletonList(__.length()); + } + + @Test + public void testReturnTypes() { + assertEquals(Integer.valueOf(4), __.__("test").length().next()); + assertArrayEquals(new Integer[]{5, 4, null, 0}, __.inject("hello", "test", null, "").length().toList().toArray()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowWithIncomingArrayList() { + __.__(Arrays.asList("a", "b", "c")).length().next(); + } + +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStepTest.java new file mode 100644 index 0000000000..edba0558e9 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerStepTest.java @@ -0,0 +1,55 @@ +/* + * 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.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.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * @author Yang Xia (http://github.com/xiazcy) + */ +public class ToLowerStepTest extends StepTest { + + @Override + protected List<Traversal> getTraversals() { + return Collections.singletonList(__.toLower()); + } + + @Test + public void testReturnTypes() { + assertEquals("test", __.__("TEST").toLower().next()); + assertArrayEquals(new String[]{"hello", "test", "no.123", null, ""}, + __.inject("hElLo", "TEST", "NO.123", null, "").toLower().toList().toArray()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowWithIncomingArrayList() { + __.__(Arrays.asList("a", "b", "c")).toLower().next(); + } + +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStepTest.java new file mode 100644 index 0000000000..3d9cdd9447 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperStepTest.java @@ -0,0 +1,55 @@ +/* + * 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.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.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * @author Yang Xia (http://github.com/xiazcy) + */ +public class ToUpperStepTest extends StepTest { + + @Override + protected List<Traversal> getTraversals() { + return Collections.singletonList(__.toUpper()); + } + + @Test + public void testReturnTypes() { + assertEquals("TEST", __.__("test").toUpper().next()); + assertArrayEquals(new String[]{"HELLO", "TEST", "NO.123", null, ""}, + __.inject("hElLo", "test", "no.123", null, "").toUpper().toList().toArray()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowWithIncomingArrayList() { + __.__(Arrays.asList("a", "b", "c")).toUpper().next(); + } + +} diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index f32dcae128..4c320407ae 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -203,6 +203,15 @@ namespace Gremlin.Net.Process.Traversal Bytecode.AddStep("as", args.ToArray()); return Wrap<TStart, TEnd>(this); } + + /// <summary> + /// Adds the asString step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal<TStart, string> AsString () + { + Bytecode.AddStep("asString"); + return Wrap<TStart, string>(this); + } /// <summary> /// Adds the barrier step to this <see cref="GraphTraversal{SType, EType}" />. @@ -1115,6 +1124,15 @@ namespace Gremlin.Net.Process.Traversal Bytecode.AddStep("label"); return Wrap<TStart, string>(this); } + + /// <summary> + /// Adds the length step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal<TStart, int?> Length () + { + Bytecode.AddStep("length"); + return Wrap<TStart, int?>(this); + } /// <summary> /// Adds the limit step to this <see cref="GraphTraversal{SType, EType}" />. @@ -1932,6 +1950,24 @@ namespace Gremlin.Net.Process.Traversal Bytecode.AddStep("toE", args.ToArray()); return Wrap<TStart, Edge>(this); } + + /// <summary> + /// Adds the toLower step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal<TStart, string?> ToLower () + { + Bytecode.AddStep("toLower"); + return Wrap<TStart, string?>(this); + } + + /// <summary> + /// Adds the toUpper step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal<TStart, string?> ToUpper () + { + Bytecode.AddStep("toUpper"); + return Wrap<TStart, string?>(this); + } /// <summary> /// Adds the toV step to this <see cref="GraphTraversal{SType, EType}" />. diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs index 82b147cd75..354aea1450 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs @@ -134,6 +134,14 @@ namespace Gremlin.Net.Process.Traversal ? new GraphTraversal<object, object>().As(label) : new GraphTraversal<object, object>().As(label, labels); } + + /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the asString step to that traversal. + /// </summary> + public static GraphTraversal<object, string> AsString() + { + return new GraphTraversal<object, string>().AsString(); + } /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the barrier step to that traversal. @@ -772,6 +780,14 @@ namespace Gremlin.Net.Process.Traversal { return new GraphTraversal<object, string>().Label(); } + + /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the length step to that traversal. + /// </summary> + public static GraphTraversal<object, int?> Length() + { + return new GraphTraversal<object, int?>().Length(); + } /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the limit step to that traversal. @@ -1348,6 +1364,22 @@ namespace Gremlin.Net.Process.Traversal ? new GraphTraversal<object, Edge>().ToE(direction) : new GraphTraversal<object, Edge>().ToE(direction, edgeLabels); } + + /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the toLower step to that traversal. + /// </summary> + public static GraphTraversal<object, string?> ToLower() + { + return new GraphTraversal<object, string?>().ToLower(); + } + + /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the ToUpper step to that traversal. + /// </summary> + public static GraphTraversal<object, string?> ToUpper() + { + return new GraphTraversal<object, string?>().ToUpper(); + } /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the toV step to that traversal. diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs index db99a831e2..ab453a49f8 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -57,6 +57,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin private static readonly IDictionary<Regex, Func<string, string, object?>> Parsers = new Dictionary<string, Func<string, string, object?>> { + {@"str\[(.*)\]", (x, graphName) => x }, //returns the string value as is {@"vp\[(.+)\]", ToVertexProperty}, {@"d\[(.*)\]\.([bsilfdmn])", ToNumber}, {@"D\[(.+)\]", ToDirection}, diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 140a6fb68f..997075ba42 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -470,6 +470,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_addVXpersonX_propertyXname_joshX_propertyXage_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","josh").Property("age",null), (g,p) =>g.V().Has("person","age",(object) null)}}, {"g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("friendWeight",null,"acl",null), (g,p) =>g.V().Has("person","name","marko").Has("friendWeight",(object) null), (g,p) =>g.V().Has("person","name","marko").Properties<object>("friendWeight").Has("acl",(object) null), (g,p) =>g.V().Has("person","name","marko").Prop [...] {"g_V_hasXperson_name_aliceX_propertyXsingle_age_unionXage_constantX1XX_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","alice").Property(Cardinality.Single,"age",50), (g,p) =>g.V().Has("person","name","alice").Property("age",__.Union<object>(__.Values<object>("age"),__.Constant<object>(1)).Sum<object>()), (g,p) =>g.V().Has("person","age",50), (g,p) =>g.V().Has("person","age",51)}}, + {"g_injectX1_2X_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(1,2).AsString()}}, + {"g_injectX1_nullX_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1,null).AsString()}}, + {"g_V_valueMapXnameX_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>("name").AsString()}}, + {"g_V_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().AsString()}}, + {"g_E_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E().AsString()}}, + {"g_V_properties", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Properties<object>().AsString()}}, + {"g_V_hasLabelXpersonX_valuesXageX_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("age").AsString()}}, + {"g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("age").AsString().Concat<object>(" years old")}}, {"g_call", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Call<object>()}}, {"g_callXlistX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Call<object>("--list")}}, {"g_callXlistX_withXstring_stringX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Call<object>("--list").With("service","tinker.search")}}, @@ -562,6 +570,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Fold().Order(Scope.Local).Index<object>().Unfold<object>().Order().By(__.Tail<object>(Scope.Local,1))}}, {"g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").Fold().Order(Scope.Local).Index<object>().With("~tinkerpop.index.indexer",1)}}, {"g_VX1X_valuesXageX_index_unfold_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Index<object>().Unfold<object>().Unfold<object>()}}, + {"g_injectXfeature_test_nullX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature","test",null).Length()}}, + {"g_injectXListXa_bXX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Length()}}, + {"g_V_valuesXnameX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Length()}}, {"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX3XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").Or().Loops().Is(3)).Has("name","peter").Path().By("name")}}, {"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX2XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").Or().Loops().Is(2)).Has("name","peter").Path().By("name")}}, {"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_and_loops_isX3XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").And().Loops().Is(3)).Has("name","peter").Path().By("name")}}, @@ -926,6 +937,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_injectXnull_10_5_nullX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Sum<object>()}}, {"g_injectXlistXnull_10_5_nullXX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Sum<object>(Scope.Local)}}, {"g_VX1X_valuesXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Sum<object>(Scope.Local)}}, + {"g_injectXfeature_test_nullX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("FEATURE","tESt",null).ToLower()}}, + {"g_injectXListXa_bXX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).ToLower()}}, + {"g_V_valuesXnameX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","MARKO").Property("age",29).As("marko").AddV("person").Property("name","VADAS").Property("age",27).As("vadas").AddV("software").Property("name","LOP").Property("lang","java").As("lop").AddV("person").Property("name","JOSH").Property("age",32).As("josh").AddV("software").Property("name","RIPPLE").Property("lang","java").As("rip [...] + {"g_injectXfeature_test_nullX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature","tESt",null).ToUpper()}}, + {"g_injectXfeature_test_nullX_asString_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature","tESt",null).AsString().ToUpper()}}, + {"g_injectXListXa_bXX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).ToUpper()}}, + {"g_V_valuesXnameX_toUpper", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").ToUpper()}}, {"g_V_localXoutE_foldX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.OutE().Fold()).Unfold<object>()}}, {"g_V_valueMap_unfold_mapXkeyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>().Unfold<object>().Map<object>((IFunction) p["l1"])}}, {"g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.HasId(p["vid6"])).Path().By("name").Unfold<object>()}}, diff --git a/gremlin-go/driver/anonymousTraversal.go b/gremlin-go/driver/anonymousTraversal.go index a6a03ecc17..8e57f347fb 100644 --- a/gremlin-go/driver/anonymousTraversal.go +++ b/gremlin-go/driver/anonymousTraversal.go @@ -59,6 +59,8 @@ type AnonymousTraversal interface { And(args ...interface{}) *GraphTraversal // As adds the as step to the GraphTraversal. As(args ...interface{}) *GraphTraversal + // AsString adds the asString step to the GraphTraversal. + AsString(args ...interface{}) *GraphTraversal // Barrier adds the barrier step to the GraphTraversal. Barrier(args ...interface{}) *GraphTraversal // Both adds the both step to the GraphTraversal. @@ -147,6 +149,8 @@ type AnonymousTraversal interface { Key(args ...interface{}) *GraphTraversal // Label adds the label step to the GraphTraversal. Label(args ...interface{}) *GraphTraversal + // Length adds the length step to the GraphTraversal. + Length(args ...interface{}) *GraphTraversal // Limit adds the limit step to the GraphTraversal. Limit(args ...interface{}) *GraphTraversal // Local adds the local step to the GraphTraversal. @@ -243,6 +247,10 @@ type AnonymousTraversal interface { To(args ...interface{}) *GraphTraversal // ToE adds the toE step to the GraphTraversal. ToE(args ...interface{}) *GraphTraversal + // ToLower adds the toLower step to the GraphTraversal. + ToLower(args ...interface{}) *GraphTraversal + // ToUpper adds the toUpper step to the GraphTraversal. + ToUpper(args ...interface{}) *GraphTraversal // ToV adds the toV step to the GraphTraversal. ToV(args ...interface{}) *GraphTraversal // Tree adds the tree step to the GraphTraversal. @@ -317,6 +325,11 @@ func (anonymousTraversal *anonymousTraversal) As(args ...interface{}) *GraphTrav return anonymousTraversal.graphTraversal().As(args...) } +// AsString adds the asString step to the GraphTraversal. +func (anonymousTraversal *anonymousTraversal) AsString(args ...interface{}) *GraphTraversal { + return anonymousTraversal.graphTraversal().AsString(args...) +} + // Barrier adds the barrier step to the GraphTraversal. func (anonymousTraversal *anonymousTraversal) Barrier(args ...interface{}) *GraphTraversal { return anonymousTraversal.graphTraversal().Barrier(args...) @@ -537,6 +550,11 @@ func (anonymousTraversal *anonymousTraversal) Label(args ...interface{}) *GraphT return anonymousTraversal.graphTraversal().Label(args...) } +// Length adds the length step to the GraphTraversal. +func (anonymousTraversal *anonymousTraversal) Length(args ...interface{}) *GraphTraversal { + return anonymousTraversal.graphTraversal().Length(args...) +} + // Limit adds the limit step to the GraphTraversal. func (anonymousTraversal *anonymousTraversal) Limit(args ...interface{}) *GraphTraversal { return anonymousTraversal.graphTraversal().Limit(args...) @@ -777,6 +795,16 @@ func (anonymousTraversal *anonymousTraversal) ToE(args ...interface{}) *GraphTra return anonymousTraversal.graphTraversal().ToE(args...) } +// ToLower adds the toLower step to the GraphTraversal. +func (anonymousTraversal *anonymousTraversal) ToLower(args ...interface{}) *GraphTraversal { + return anonymousTraversal.graphTraversal().ToLower(args...) +} + +// ToUpper adds the toUpper step to the GraphTraversal. +func (anonymousTraversal *anonymousTraversal) ToUpper(args ...interface{}) *GraphTraversal { + return anonymousTraversal.graphTraversal().ToUpper(args...) +} + // ToV adds the toV step to the GraphTraversal. func (anonymousTraversal *anonymousTraversal) ToV(args ...interface{}) *GraphTraversal { return anonymousTraversal.graphTraversal().ToV(args...) diff --git a/gremlin-go/driver/cucumber/cucumberSteps_test.go b/gremlin-go/driver/cucumber/cucumberSteps_test.go index c8c217037d..dee57762a5 100644 --- a/gremlin-go/driver/cucumber/cucumberSteps_test.go +++ b/gremlin-go/driver/cucumber/cucumberSteps_test.go @@ -46,6 +46,7 @@ var toListLock sync.Mutex func init() { parsers = map[*regexp.Regexp]func(string, string) interface{}{ + regexp.MustCompile(`^str\[(.*)]$`): func(stringVal, graphName string) interface{} { return stringVal }, //returns the string value as is regexp.MustCompile(`^d\[(.*)]\.[bslfdmn]$`): toNumeric, regexp.MustCompile(`^d\[(.*)]\.[i]$`): toInt32, regexp.MustCompile(`^vp\[(.+)]$`): toVertexProperty, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 6019e19db8..e3e6665009 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -440,6 +440,14 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_addVXpersonX_propertyXname_joshX_propertyXage_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "josh").Property("age", nil)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "age", nil)}}, "g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("friendWeight", nil, "acl", nil)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "marko").Has("friendWeight", nil)}, func(g *gremlingo.GraphTraversalSource, p map[string]interfa [...] "g_V_hasXperson_name_aliceX_propertyXsingle_age_unionXage_constantX1XX_sumX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "alice").Property(gremlingo.Cardinality.Single, "age", 50)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "alice").Property("age", gremlingo.T__.Union(gremlingo.T__.Values("age"), gremlingo.T_ [...] + "g_injectX1_2X_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(1, 2).AsString()}}, + "g_injectX1_nullX_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(1, nil).AsString()}}, + "g_V_valueMapXnameX_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap("name").AsString()}}, + "g_V_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().AsString()}}, + "g_E_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().AsString()}}, + "g_V_properties": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties().AsString()}}, + "g_V_hasLabelXpersonX_valuesXageX_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("age").AsString()}}, + "g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("age").AsString().Concat(" years old")}}, "g_call": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Call()}}, "g_callXlistX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Call("--list")}}, "g_callXlistX_withXstring_stringX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Call("--list").With("service", "tinker.search")}}, @@ -532,6 +540,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Fold().Order(gremlingo.Scope.Local).Index().Unfold().Order().By(gremlingo.T__.Tail(gremlingo.Scope.Local, 1))}}, "g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Fold().Order(gremlingo.Scope.Local).Index().With("~tinkerpop.index.indexer", 1)}}, "g_VX1X_valuesXageX_index_unfold_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Index().Unfold().Unfold()}}, + "g_injectXfeature_test_nullX_length": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("feature", "test", nil).Length()}}, + "g_injectXListXa_bXX_length": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Length()}}, + "g_V_valuesXnameX_length": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Length()}}, "g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX3XX_hasXname_peterX_path_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Repeat(gremlingo.T__.Both().SimplePath()).Until(gremlingo.T__.Has("name", "peter").Or().Loops().Is(3)).Has("name", "peter").Path().By("name")}}, "g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX2XX_hasXname_peterX_path_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Repeat(gremlingo.T__.Both().SimplePath()).Until(gremlingo.T__.Has("name", "peter").Or().Loops().Is(2)).Has("name", "peter").Path().By("name")}}, "g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_and_loops_isX3XX_hasXname_peterX_path_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Repeat(gremlingo.T__.Both().SimplePath()).Until(gremlingo.T__.Has("name", "peter").And().Loops().Is(3)).Has("name", "peter").Path().By("name")}}, @@ -896,6 +907,13 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_injectXnull_10_5_nullX_sum": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, p["xx1"], p["xx2"], nil).Sum()}}, "g_injectXlistXnull_10_5_nullXX_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Sum(gremlingo.Scope.Local)}}, "g_VX1X_valuesXageX_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Sum(gremlingo.Scope.Local)}}, + "g_injectXfeature_test_nullX_toLower": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("FEATURE", "tESt", nil).ToLower()}}, + "g_injectXListXa_bXX_toLower": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).ToLower()}}, + "g_V_valuesXnameX_toLower": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "MARKO").Property("age", 29).As("marko").AddV("person").Property("name", "VADAS").Property("age", 27).As("vadas").AddV("software").Property("name", "LOP").Property("lang", "java").As("lop").AddV("person").Property("name", "JOSH").Property("age", 32).As("josh").AddV("software").Property("name", "RIPPLE").Property("lang", "ja [...] + "g_injectXfeature_test_nullX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("feature", "tESt", nil).ToUpper()}}, + "g_injectXfeature_test_nullX_asString_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("feature", "tESt", nil).AsString().ToUpper()}}, + "g_injectXListXa_bXX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).ToUpper()}}, + "g_V_valuesXnameX_toUpper": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").ToUpper()}}, "g_V_localXoutE_foldX_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.OutE().Fold()).Unfold()}}, "g_V_valueMap_unfold_mapXkeyX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap().Unfold().Map(p["l1"])}}, "g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Repeat(gremlingo.T__.Both().SimplePath()).Until(gremlingo.T__.HasId(p["vid6"])).Path().By("name").Unfold()}}, diff --git a/gremlin-go/driver/graph.go b/gremlin-go/driver/graph.go index ade4c32ab3..3f7910274b 100644 --- a/gremlin-go/driver/graph.go +++ b/gremlin-go/driver/graph.go @@ -32,8 +32,8 @@ type Graph struct { // Element is the base structure for both Vertex and Edge. // The inherited identifier must be unique to the inheriting classes. type Element struct { - Id interface{} - Label string + Id interface{} + Label string Properties interface{} } @@ -75,12 +75,12 @@ type Path struct { // String returns the string representation of the vertex. func (v *Vertex) String() string { - return fmt.Sprintf("v[%s]", v.Id) + return fmt.Sprintf("v[%v]", v.Id) } // String returns the string representation of the edge. func (e *Edge) String() string { - return fmt.Sprintf("e[%s][%s-%s->%s]", e.Id, e.OutV.Id, e.Label, e.InV.Id) + return fmt.Sprintf("e[%v][%v-%s->%v]", e.Id, e.OutV.Id, e.Label, e.InV.Id) } // String returns the string representation of the vertex property. diff --git a/gremlin-go/driver/graphTraversal.go b/gremlin-go/driver/graphTraversal.go index 797774ec3e..4e651583fc 100644 --- a/gremlin-go/driver/graphTraversal.go +++ b/gremlin-go/driver/graphTraversal.go @@ -94,6 +94,12 @@ func (g *GraphTraversal) As(args ...interface{}) *GraphTraversal { return g } +// AsString adds the asString step to the GraphTraversal. +func (g *GraphTraversal) AsString(args ...interface{}) *GraphTraversal { + g.Bytecode.AddStep("asString", args...) + return g +} + // Barrier adds the barrier step to the GraphTraversal. func (g *GraphTraversal) Barrier(args ...interface{}) *GraphTraversal { // Force int32 serialization for valid number values for server compatibility @@ -361,6 +367,12 @@ func (g *GraphTraversal) Label(args ...interface{}) *GraphTraversal { return g } +// Length adds the length step to the GraphTraversal. +func (g *GraphTraversal) Length(args ...interface{}) *GraphTraversal { + g.Bytecode.AddStep("length", args...) + return g +} + // Limit adds the limit step to the GraphTraversal. func (g *GraphTraversal) Limit(args ...interface{}) *GraphTraversal { g.Bytecode.AddStep("limit", args...) @@ -651,6 +663,18 @@ func (g *GraphTraversal) ToE(args ...interface{}) *GraphTraversal { return g } +// ToLower adds the toLower step to the GraphTraversal. +func (g *GraphTraversal) ToLower(args ...interface{}) *GraphTraversal { + g.Bytecode.AddStep("toLower", args...) + return g +} + +// ToUpper adds the toUpper step to the GraphTraversal. +func (g *GraphTraversal) ToUpper(args ...interface{}) *GraphTraversal { + g.Bytecode.AddStep("toUpper", args...) + return g +} + // ToV adds the toV step to the GraphTraversal. func (g *GraphTraversal) ToV(args ...interface{}) *GraphTraversal { g.Bytecode.AddStep("toV", args...) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js index b14a9e578c..114cf9503f 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js @@ -434,6 +434,16 @@ class GraphTraversal extends Traversal { return this; } + /** + * Graph traversal asString method. + * @param {...Object} args + * @returns {GraphTraversal} + */ + asString(...args) { + this.bytecode.addStep('asString', args); + return this; + } + /** * Graph traversal barrier method. * @param {...Object} args @@ -872,6 +882,16 @@ class GraphTraversal extends Traversal { return this; } + /** + * Graph traversal length method. + * @param {...Object} args + * @returns {GraphTraversal} + */ + length(...args) { + this.bytecode.addStep('length', args); + return this; + } + /** * Graph traversal limit method. * @param {...Object} args @@ -1352,6 +1372,26 @@ class GraphTraversal extends Traversal { return this; } + /** + * Graph traversal toLower method. + * @param {...Object} args + * @returns {GraphTraversal} + */ + toLower(...args) { + this.bytecode.addStep('toLower', args); + return this; + } + + /** + * Graph traversal toUpper method. + * @param {...Object} args + * @returns {GraphTraversal} + */ + toUpper(...args) { + this.bytecode.addStep('toUpper', args); + return this; + } + /** * Graph traversal toV method. * @param {...Object} args @@ -1519,6 +1559,7 @@ const statics = { aggregate: (...args) => callOnEmptyTraversal('aggregate', args), and: (...args) => callOnEmptyTraversal('and', args), as: (...args) => callOnEmptyTraversal('as', args), + asString: (...args) => callOnEmptyTraversal('asString', args), barrier: (...args) => callOnEmptyTraversal('barrier', args), both: (...args) => callOnEmptyTraversal('both', args), bothE: (...args) => callOnEmptyTraversal('bothE', args), @@ -1560,6 +1601,7 @@ const statics = { is: (...args) => callOnEmptyTraversal('is', args), key: (...args) => callOnEmptyTraversal('key', args), label: (...args) => callOnEmptyTraversal('label', args), + length: (...args) => callOnEmptyTraversal('length', args), limit: (...args) => callOnEmptyTraversal('limit', args), local: (...args) => callOnEmptyTraversal('local', args), loops: (...args) => callOnEmptyTraversal('loops', args), @@ -1600,6 +1642,8 @@ const statics = { times: (...args) => callOnEmptyTraversal('times', args), to: (...args) => callOnEmptyTraversal('to', args), toE: (...args) => callOnEmptyTraversal('toE', args), + toLower: (...args) => callOnEmptyTraversal('toLower', args), + toUpper: (...args) => callOnEmptyTraversal('toUpper', args), toV: (...args) => callOnEmptyTraversal('toV', args), tree: (...args) => callOnEmptyTraversal('tree', args), unfold: (...args) => callOnEmptyTraversal('unfold', args), 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 a44ebd6298..e67755c264 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 @@ -43,6 +43,7 @@ const direction = traversalModule.direction; const merge = traversalModule.merge; const parsers = [ + [ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string value as is [ 'vp\\[(.+)\\]', toVertexProperty ], [ 'd\\[(.*)\\]\\.[bsilfdmn]', toNumeric ], [ 'v\\[(.+)\\]', toVertex ], diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index f5984bb91b..e1d6d9c1b9 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -460,6 +460,14 @@ const gremlins = { g_addVXpersonX_propertyXname_joshX_propertyXage_nullX: [function({g}) { return g.addV("person").property("name","josh").property("age",null) }, function({g}) { return g.V().has("person","age",null) }], g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX: [function({g}) { return g.addV("person").property("name","marko").property("friendWeight",null,"acl",null) }, function({g}) { return g.V().has("person","name","marko").has("friendWeight",null) }, function({g}) { return g.V().has("person","name","marko").properties("friendWeight").has("acl",null) }, function({g}) { return g.V().has("person","name","marko").properties("friendWeight").count() }], g_V_hasXperson_name_aliceX_propertyXsingle_age_unionXage_constantX1XX_sumX: [function({g}) { return g.addV("person").property("name","alice").property(Cardinality.single,"age",50) }, function({g}) { return g.V().has("person","name","alice").property("age",__.union(__.values("age"),__.constant(1)).sum()) }, function({g}) { return g.V().has("person","age",50) }, function({g}) { return g.V().has("person","age",51) }], + g_injectX1_2X_asString: [function({g}) { return g.inject(1,2).asString() }], + g_injectX1_nullX_asString: [function({g}) { return g.inject(1,null).asString() }], + g_V_valueMapXnameX_asString: [function({g}) { return g.V().valueMap("name").asString() }], + g_V_asString: [function({g}) { return g.V().asString() }], + g_E_asString: [function({g}) { return g.E().asString() }], + g_V_properties: [function({g}) { return g.V().properties().asString() }], + g_V_hasLabelXpersonX_valuesXageX_asString: [function({g}) { return g.V().hasLabel("person").values("age").asString() }], + g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX: [function({g}) { return g.V().hasLabel("person").values("age").asString().concat(" years old") }], g_call: [function({g}) { return g.call() }], g_callXlistX: [function({g}) { return g.call("--list") }], g_callXlistX_withXstring_stringX: [function({g}) { return g.call("--list").with_("service","tinker.search") }], @@ -552,6 +560,9 @@ const gremlins = { g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX: [function({g}) { return g.V().hasLabel("software").values("name").fold().order(Scope.local).index().unfold().order().by(__.tail(Scope.local,1)) }], g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX: [function({g}) { return g.V().hasLabel("person").values("name").fold().order(Scope.local).index().with_("~tinkerpop.index.indexer",1) }], g_VX1X_valuesXageX_index_unfold_unfold: [function({g, vid1}) { return g.V(vid1).values("age").index().unfold().unfold() }], + g_injectXfeature_test_nullX_length: [function({g}) { return g.inject("feature","test",null).length() }], + g_injectXListXa_bXX_length: [function({g, xx1}) { return g.inject(xx1).length() }], + g_V_valuesXnameX_length: [function({g}) { return g.V().values("name").length() }], g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX3XX_hasXname_peterX_path_byXnameX: [function({g, vid1}) { return g.V(vid1).repeat(__.both().simplePath()).until(__.has("name","peter").or().loops().is(3)).has("name","peter").path().by("name") }], g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX2XX_hasXname_peterX_path_byXnameX: [function({g, vid1}) { return g.V(vid1).repeat(__.both().simplePath()).until(__.has("name","peter").or().loops().is(2)).has("name","peter").path().by("name") }], g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_and_loops_isX3XX_hasXname_peterX_path_byXnameX: [function({g, vid1}) { return g.V(vid1).repeat(__.both().simplePath()).until(__.has("name","peter").and().loops().is(3)).has("name","peter").path().by("name") }], @@ -916,6 +927,13 @@ const gremlins = { g_injectXnull_10_5_nullX_sum: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).sum() }], g_injectXlistXnull_10_5_nullXX_sumXlocalX: [function({g, xx1}) { return g.inject(xx1).sum(Scope.local) }], g_VX1X_valuesXageX_sumXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").sum(Scope.local) }], + g_injectXfeature_test_nullX_toLower: [function({g}) { return g.inject("FEATURE","tESt",null).toLower() }], + g_injectXListXa_bXX_toLower: [function({g, xx1}) { return g.inject(xx1).toLower() }], + g_V_valuesXnameX_toLower: [function({g}) { return g.addV("person").property("name","MARKO").property("age",29).as("marko").addV("person").property("name","VADAS").property("age",27).as("vadas").addV("software").property("name","LOP").property("lang","java").as("lop").addV("person").property("name","JOSH").property("age",32).as("josh").addV("software").property("name","RIPPLE").property("lang","java").as("ripple").addV("person").property("name","PETER").property("age",35).as("peter"). [...] + g_injectXfeature_test_nullX_toUpper: [function({g}) { return g.inject("feature","tESt",null).toUpper() }], + g_injectXfeature_test_nullX_asString_toUpper: [function({g}) { return g.inject("feature","tESt",null).asString().toUpper() }], + g_injectXListXa_bXX_toUpper: [function({g, xx1}) { return g.inject(xx1).toUpper() }], + g_V_valuesXnameX_toUpper: [function({g}) { return g.V().values("name").toUpper() }], g_V_localXoutE_foldX_unfold: [function({g}) { return g.V().local(__.outE().fold()).unfold() }], g_V_valueMap_unfold_mapXkeyX: [function({g, l1}) { return g.V().valueMap().unfold().map(l1) }], g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold: [function({g, vid6, vid1}) { return g.V(vid1).repeat(__.both().simplePath()).until(__.hasId(vid6)).path().by("name").unfold() }], diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index d4c4612af2..cd67d3aa6e 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -284,7 +284,12 @@ traversalMethod | traversalMethod_element | traversalMethod_call | traversalMethod_concat + | traversalMethod_asString + | traversalMethod_toUpper + | traversalMethod_toLower + | traversalMethod_length ; + traversalMethod_V : 'V' LPAREN genericLiteralVarargs RPAREN ; @@ -809,6 +814,22 @@ traversalMethod_concat | 'concat' LPAREN stringLiteralVarargs RPAREN #traversalMethod_concat_String ; +traversalMethod_asString + : 'asString' LPAREN RPAREN #traversalMethod_asString_Empty + ; + +traversalMethod_toUpper + : 'toUpper' LPAREN RPAREN #traversalMethod_toUpper_Empty + ; + +traversalMethod_toLower + : 'toLower' LPAREN RPAREN #traversalMethod_toLower_Empty + ; + +traversalMethod_length + : 'length' LPAREN RPAREN #traversalMethod_length_Empty + ; + /********************************************* ARGUMENT AND TERMINAL RULES **********************************************/ diff --git a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py index 82a7926feb..47b6e0a9b7 100644 --- a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py +++ b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py @@ -180,7 +180,7 @@ class GraphTraversalSource(object): return self.with_computer(graph_computer, workers, result, persist, vertices, edges, configuration) def with_computer(self, graph_computer=None, workers=None, result=None, persist=None, vertices=None, - edges=None, configuration=None): + edges=None, configuration=None): return self.with_strategies( VertexProgramStrategy(graph_computer, workers, result, persist, vertices, edges, configuration)) @@ -317,6 +317,10 @@ class GraphTraversal(Traversal): self.bytecode.add_step("as", *args) return self + def as_string(self, *args): + self.bytecode.add_step("asString", *args) + return self + def barrier(self, *args): self.bytecode.add_step("barrier", *args) return self @@ -591,6 +595,10 @@ class GraphTraversal(Traversal): self.bytecode.add_step("label", *args) return self + def length(self, *args): + self.bytecode.add_step("length", *args) + return self + def limit(self, *args): self.bytecode.add_step("limit", *args) return self @@ -860,6 +868,14 @@ class GraphTraversal(Traversal): self.bytecode.add_step("toE", *args) return self + def to_lower(self, *args): + self.bytecode.add_step("toLower", *args) + return self + + def to_upper(self, *args): + self.bytecode.add_step("toUpper", *args) + return self + def toV(self, *args): warnings.warn( "gremlin_python.process.GraphTraversalSource.toV will be replaced by " @@ -983,6 +999,10 @@ class __(object, metaclass=MagicType): def as_(cls, *args): return cls.graph_traversal(None, None, Bytecode()).as_(*args) + @classmethod + def as_string(cls, *args): + return cls.graph_traversal(None, None, Bytecode()).as_string(*args) + @classmethod def barrier(cls, *args): return cls.graph_traversal(None, None, Bytecode()).barrier(*args) @@ -1251,6 +1271,10 @@ class __(object, metaclass=MagicType): def label(cls, *args): return cls.graph_traversal(None, None, Bytecode()).label(*args) + @classmethod + def length(cls, *args): + return cls.graph_traversal(None, None, Bytecode()).length(*args) + @classmethod def limit(cls, *args): return cls.graph_traversal(None, None, Bytecode()).limit(*args) @@ -1475,6 +1499,14 @@ class __(object, metaclass=MagicType): def to_e(cls, *args): return cls.graph_traversal(None, None, Bytecode()).to_e(*args) + @classmethod + def to_lower(cls, *args): + return cls.graph_traversal(None, None, Bytecode()).to_lower(*args) + + @classmethod + def to_upper(cls, *args): + return cls.graph_traversal(None, None, Bytecode()).to_upper(*args) + @classmethod def toV(cls, *args): warnings.warn( @@ -1639,6 +1671,10 @@ def as_(*args): return __.as_(*args) +def as_string(*args): + return __.as_string(*args) + + def barrier(*args): return __.barrier(*args) @@ -1855,6 +1891,10 @@ def label(*args): return __.label(*args) +def length(*args): + return __.length(*args) + + def limit(*args): return __.limit(*args) @@ -2047,6 +2087,14 @@ def to_e(*args): return __.to_e(*args) +def to_lower(*args): + return __.to_lower(*args) + + +def to_upper(*args): + return __.to_upper(*args) + + def toV(*args): return __.to_v(*args) @@ -2107,6 +2155,8 @@ statics.add_static('and_', and_) statics.add_static('as_', as_) +statics.add_static('as_string', as_string) + statics.add_static('barrier', barrier) statics.add_static('both', both) @@ -2213,6 +2263,8 @@ statics.add_static('key', key) statics.add_static('label', label) +statics.add_static('length', length) + statics.add_static('limit', limit) statics.add_static('local', local) @@ -2311,6 +2363,10 @@ statics.add_static('to_e', to_e) statics.add_static('toV', toV) +statics.add_static('to_lower', to_lower) + +statics.add_static('to_upper', to_upper) + statics.add_static('to_v', to_v) statics.add_static('tree', tree) diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py index 9b4cd42727..fb0f7829ae 100644 --- a/gremlin-python/src/main/python/radish/feature_steps.py +++ b/gremlin-python/src/main/python/radish/feature_steps.py @@ -194,6 +194,8 @@ def _convert(val, ctx): # convert to tuple key if list/set as neither are hashable n[tuple(k) if isinstance(k, (set, list)) else k] = _convert(value, ctx) return n + elif isinstance(val, str) and re.match(r"^str\[.*\]$", val): # return string as is + return val[4:-1] elif isinstance(val, str) and re.match(r"^l\[.*\]$", val): # parse list return [] if val == "l[]" else list(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) elif isinstance(val, str) and re.match(r"^s\[.*\]$", val): # parse set diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 829d4492b3..1bc61f3ed4 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -441,6 +441,14 @@ world.gremlins = { 'g_addVXpersonX_propertyXname_joshX_propertyXage_nullX': [(lambda g:g.addV('person').property('name','josh').property('age',None)), (lambda g:g.V().has('person','age',None))], 'g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX': [(lambda g:g.addV('person').property('name','marko').property('friendWeight',None,'acl',None)), (lambda g:g.V().has('person','name','marko').has('friendWeight',None)), (lambda g:g.V().has('person','name','marko').properties('friendWeight').has('acl',None)), (lambda g:g.V().has('person','name','marko').properties('friendWeight').count())], 'g_V_hasXperson_name_aliceX_propertyXsingle_age_unionXage_constantX1XX_sumX': [(lambda g:g.addV('person').property('name','alice').property(Cardinality.single,'age',50)), (lambda g:g.V().has('person','name','alice').property('age',__.union(__.age,__.constant(1)).sum_())), (lambda g:g.V().has('person','age',50)), (lambda g:g.V().has('person','age',51))], + 'g_injectX1_2X_asString': [(lambda g:g.inject(1,2).as_string())], + 'g_injectX1_nullX_asString': [(lambda g:g.inject(1,None).as_string())], + 'g_V_valueMapXnameX_asString': [(lambda g:g.V().valueMap('name').as_string())], + 'g_V_asString': [(lambda g:g.V().as_string())], + 'g_E_asString': [(lambda g:g.E().as_string())], + 'g_V_properties': [(lambda g:g.V().properties().as_string())], + 'g_V_hasLabelXpersonX_valuesXageX_asString': [(lambda g:g.V().hasLabel('person').age.as_string())], + 'g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX': [(lambda g:g.V().hasLabel('person').age.as_string().concat(' years old'))], 'g_call': [(lambda g:g.call())], 'g_callXlistX': [(lambda g:g.call('--list'))], 'g_callXlistX_withXstring_stringX': [(lambda g:g.call('--list').with_('service','tinker.search'))], @@ -533,6 +541,9 @@ world.gremlins = { 'g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX': [(lambda g:g.V().hasLabel('software').name.fold().order(Scope.local).index().unfold().order().by(__.tail(Scope.local,1)))], 'g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX': [(lambda g:g.V().hasLabel('person').name.fold().order(Scope.local).index().with_('~tinkerpop.index.indexer',1))], 'g_VX1X_valuesXageX_index_unfold_unfold': [(lambda g, vid1=None:g.V(vid1).age.index().unfold().unfold())], + 'g_injectXfeature_test_nullX_length': [(lambda g:g.inject('feature','test',None).length())], + 'g_injectXListXa_bXX_length': [(lambda g, xx1=None:g.inject(xx1).length())], + 'g_V_valuesXnameX_length': [(lambda g:g.V().name.length())], 'g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX3XX_hasXname_peterX_path_byXnameX': [(lambda g, vid1=None:g.V(vid1).repeat(__.both().simplePath()).until(__.has('name','peter').or_().loops().is_(3)).has('name','peter').path().by('name'))], 'g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX2XX_hasXname_peterX_path_byXnameX': [(lambda g, vid1=None:g.V(vid1).repeat(__.both().simplePath()).until(__.has('name','peter').or_().loops().is_(2)).has('name','peter').path().by('name'))], 'g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_and_loops_isX3XX_hasXname_peterX_path_byXnameX': [(lambda g, vid1=None:g.V(vid1).repeat(__.both().simplePath()).until(__.has('name','peter').and_().loops().is_(3)).has('name','peter').path().by('name'))], @@ -897,6 +908,13 @@ world.gremlins = { 'g_injectXnull_10_5_nullX_sum': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).sum_())], 'g_injectXlistXnull_10_5_nullXX_sumXlocalX': [(lambda g, xx1=None:g.inject(xx1).sum_(Scope.local))], 'g_VX1X_valuesXageX_sumXlocalX': [(lambda g, vid1=None:g.V(vid1).age.sum_(Scope.local))], + 'g_injectXfeature_test_nullX_toLower': [(lambda g:g.inject('FEATURE','tESt',None).to_lower())], + 'g_injectXListXa_bXX_toLower': [(lambda g, xx1=None:g.inject(xx1).to_lower())], + 'g_V_valuesXnameX_toLower': [(lambda g:g.addV('person').property('name','MARKO').property('age',29).as_('marko').addV('person').property('name','VADAS').property('age',27).as_('vadas').addV('software').property('name','LOP').property('lang','java').as_('lop').addV('person').property('name','JOSH').property('age',32).as_('josh').addV('software').property('name','RIPPLE').property('lang','java').as_('ripple').addV('person').property('name','PETER').property('age',35).as_('peter').addE( [...] + 'g_injectXfeature_test_nullX_toUpper': [(lambda g:g.inject('feature','tESt',None).to_upper())], + 'g_injectXfeature_test_nullX_asString_toUpper': [(lambda g:g.inject('feature','tESt',None).as_string().to_upper())], + 'g_injectXListXa_bXX_toUpper': [(lambda g, xx1=None:g.inject(xx1).to_upper())], + 'g_V_valuesXnameX_toUpper': [(lambda g:g.V().name.to_upper())], 'g_V_localXoutE_foldX_unfold': [(lambda g:g.V().local(__.outE().fold()).unfold())], 'g_V_valueMap_unfold_mapXkeyX': [(lambda g, l1=None:g.V().valueMap().unfold().map(l1))], 'g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold': [(lambda g, vid6=None,vid1=None:g.V(vid1).repeat(__.both().simplePath()).until(__.hasId(vid6)).path().by('name').unfold())], diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java index 803320a0cd..d21d5efc64 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java @@ -164,6 +164,9 @@ public final class StepDefinition { }}; private List<Pair<Pattern, Function<String,Object>>> objectMatcherConverters = new ArrayList<Pair<Pattern, Function<String,Object>>>() {{ + // return the string values as is, used to wrap results that may contain other regex patterns + add(Pair.with(Pattern.compile("str\\[(.*)\\]"), String::valueOf)); + // expects json so that should port to the Gremlin script form - replace curly json braces with square ones // for Gremlin sake. add(Pair.with(Pattern.compile("m\\[(.*)\\]"), s -> { diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsString.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsString.feature new file mode 100644 index 0000000000..4f36f9e802 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsString.feature @@ -0,0 +1,146 @@ +# 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. + +@StepClassMap @StepAsString +Feature: Step - asString() + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1_2X_asString + Given the empty graph + And the traversal of + """ + g.inject(1, 2).asString() + """ + When iterated to list + Then the result should be unordered + | result | + | 1 | + | 2 | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1_nullX_asString + Given the empty graph + And the traversal of + """ + g.inject(1, null).asString() + """ + When iterated to list + Then the result should be unordered + | result | + | 1 | + | str[null] | + + Scenario: g_V_valueMapXnameX_asString + Given the modern graph + And the traversal of + """ + g.V().valueMap("name").asString() + """ + When iterated to list + Then the result should be unordered + | result | + | {name=[marko]} | + | {name=[vadas]} | + | {name=[lop]} | + | {name=[josh]} | + | {name=[ripple]} | + | {name=[peter]} | + + @UserSuppliedVertexIds + Scenario: g_V_asString + Given the modern graph + And the traversal of + """ + g.V().asString() + """ + When iterated to list + Then the result should be unordered + | result | + | str[v[1]] | + | str[v[2]] | + | str[v[3]] | + | str[v[4]] | + | str[v[5]] | + | str[v[6]] | + + @UserSuppliedEdgeIds + Scenario: g_E_asString + Given the modern graph + And the traversal of + """ + g.E().asString() + """ + When iterated to list + Then the result should be unordered + | result | + | str[e[7][1-knows->2]] | + | str[e[8][1-knows->4]] | + | str[e[9][1-created->3]] | + | str[e[10][4-created->5]] | + | str[e[11][4-created->3]] | + | str[e[12][6-created->3]] | + + @UserSuppliedVertexPropertyIds + Scenario: g_V_properties + Given the modern graph + And the traversal of + """ + g.V().properties().asString() + """ + When iterated to list + Then the result should be unordered + | result | + | str[vp[name->marko]] | + | str[vp[age->29]] | + | str[vp[name->vadas]] | + | str[vp[age->27]] | + | str[vp[name->lop]] | + | str[vp[lang->java]] | + | str[vp[name->josh]] | + | str[vp[age->32]] | + | str[vp[name->ripple]] | + | str[vp[lang->java]] | + | str[vp[name->peter]] | + | str[vp[age->35]] | + + Scenario: g_V_hasLabelXpersonX_valuesXageX_asString + Given the modern graph + And the traversal of + """ + g.V().hasLabel("person").values("age").asString() + """ + When iterated to list + Then the result should be unordered + | result | + | 29 | + | 27 | + | 32 | + | 35 | + + Scenario: g_V_hasLabelXpersonX_valuesXageX_asString_concatX_years_oldX + Given the modern graph + And the traversal of + """ + g.V().hasLabel("person").values("age").asString().concat(" years old") + """ + When iterated to list + Then the result should be unordered + | result | + | 29 years old | + | 27 years old | + | 32 years old | + | 35 years old | \ No newline at end of file diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Length.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Length.feature new file mode 100644 index 0000000000..460bc68539 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Length.feature @@ -0,0 +1,60 @@ +# 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. + +@StepClassMap @StepLength +Feature: Step - length() + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXfeature_test_nullX_length + Given the empty graph + And the traversal of + """ + g.inject("feature", "test", null).length() + """ + When iterated to list + Then the result should be unordered + | result | + | d[7].i | + | d[4].i | + | null | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXListXa_bXX_length + Given the empty graph + And using the parameter xx1 defined as "l[a,b]" + And the traversal of + """ + g.inject(xx1).length() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The length() step can only take string as argument" + + Scenario: g_V_valuesXnameX_length + Given the modern graph + And the traversal of + """ + g.V().values("name").length() + """ + When iterated to list + Then the result should be unordered + | result | + | d[5].i | + | d[5].i | + | d[3].i | + | d[4].i | + | d[6].i | + | d[5].i | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToLower.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToLower.feature new file mode 100644 index 0000000000..480a861b38 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToLower.feature @@ -0,0 +1,75 @@ +# 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. + +@StepClassMap @StepToLower +Feature: Step - toLower() + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXfeature_test_nullX_toLower + Given the empty graph + And the traversal of + """ + g.inject("FEATURE", "tESt", null).toLower() + """ + When iterated to list + Then the result should be unordered + | result | + | feature | + | test | + | null | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXListXa_bXX_toLower + Given the empty graph + And using the parameter xx1 defined as "l[a,b]" + And the traversal of + """ + g.inject(xx1).toLower() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The toLower() step can only take string as argument" + + Scenario: g_V_valuesXnameX_toLower + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name", "MARKO").property("age", 29).as("marko"). + addV("person").property("name", "VADAS").property("age", 27).as("vadas"). + addV("software").property("name", "LOP").property("lang", "java").as("lop"). + addV("person").property("name","JOSH").property("age", 32).as("josh"). + addV("software").property("name", "RIPPLE").property("lang", "java").as("ripple"). + addV("person").property("name", "PETER").property("age", 35).as('peter'). + addE("knows").from("marko").to("vadas").property("weight", 0.5d). + addE("knows").from("marko").to("josh").property("weight", 1.0d). + addE("created").from("marko").to("lop").property("weight", 0.4d). + addE("created").from("josh").to("ripple").property("weight", 1.0d). + addE("created").from("josh").to("lop").property("weight", 0.4d). + addE("created").from("peter").to("lop").property("weight", 0.2d) + """ + And the traversal of + """ + g.V().values("name").toLower() + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + | vadas | + | lop | + | josh | + | ripple | + | peter | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToUpper.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToUpper.feature new file mode 100644 index 0000000000..902d530fd9 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ToUpper.feature @@ -0,0 +1,74 @@ +# 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. + +@StepClassMap @StepToUpper +Feature: Step - toUpper() + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXfeature_test_nullX_toUpper + Given the empty graph + And the traversal of + """ + g.inject("feature", "tESt", null).toUpper() + """ + When iterated to list + Then the result should be unordered + | result | + | FEATURE | + | TEST | + | null | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXfeature_test_nullX_asString_toUpper + Given the empty graph + And the traversal of + """ + g.inject("feature", "tESt", null).asString().toUpper() + """ + When iterated to list + Then the result should be unordered + | result | + | FEATURE | + | TEST | + | NULL | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXListXa_bXX_toUpper + Given the empty graph + And using the parameter xx1 defined as "l[a,b]" + And the traversal of + """ + g.inject(xx1).toUpper() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The toUpper() step can only take string as argument" + + Scenario: g_V_valuesXnameX_toUpper + Given the modern graph + And the traversal of + """ + g.V().values("name").toUpper() + """ + When iterated to list + Then the result should be unordered + | result | + | MARKO | + | VADAS | + | LOP | + | JOSH | + | RIPPLE | + | PETER |