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 cd66436470 update concat() to accept traversal varargs and not treat inject as special case in argument (#2306) cd66436470 is described below commit cd664364706e0c18f561a62b133cc04c03a7ea9c Author: Yang Xia <55853655+xia...@users.noreply.github.com> AuthorDate: Mon Oct 23 16:09:48 2023 -0700 update concat() to accept traversal varargs and not treat inject as special case in argument (#2306) --- CHANGELOG.asciidoc | 3 + docs/src/dev/provider/gremlin-semantics.asciidoc | 12 ++-- docs/src/reference/the-traversal.asciidoc | 37 ++++++----- docs/src/upgrade/release-3.7.x.asciidoc | 31 ++++++++- .../grammar/DefaultGremlinBaseVisitor.java | 2 +- .../language/grammar/TraversalMethodVisitor.java | 8 ++- .../traversal/dsl/graph/GraphTraversal.java | 15 +++-- .../gremlin/process/traversal/dsl/graph/__.java | 6 +- .../process/traversal/step/map/ConcatStep.java | 73 ++++++++++++++-------- .../traversal/translator/DotNetTranslator.java | 1 - .../grammar/TraversalMethodVisitorTest.java | 28 +++++++++ .../process/traversal/step/map/ConcatStepTest.java | 10 ++- .../Process/Traversal/GraphTraversal.cs | 20 ++++-- .../src/Gremlin.Net/Process/Traversal/__.cs | 10 +-- .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 31 ++++----- gremlin-go/driver/cucumber/gremlin.go | 7 ++- .../gremlin-javascript/test/cucumber/gremlin.js | 7 ++- gremlin-language/src/main/antlr4/Gremlin.g4 | 2 +- gremlin-python/src/main/python/radish/gremlin.py | 7 ++- .../gremlin/test/features/map/Concat.feature | 41 ++++++++---- 20 files changed, 242 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index be19dd4cde..e3d6bc9e18 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -29,6 +29,9 @@ This release also includes changes from <<release-3-6-5, 3.6.6>> and <<release-3 * Added Gherkin parsing support for specific string results using `str[]`. * Added the `trim()`, `lTrim()`, `rTrim()`, and `reverse()` steps to perform `String` manipulations. * Added `replace()`, `split()` and `substring()` steps to perform `String` manipulations. +* Update `concat()` to accept `Traversal` varargs. +* Corrected `concat()` signatures in `gremlin-dotnet`, `Concat()` is now used instead of `Concat<object>()`. *(breaking)* +* Update `concat()` to not special treat `inject` in arguments and use `TraversalUtil.apply` on it as with any other child traversals. *(breaking)* * Checked graph features for meta-property support before trying to serialize them in `VertexPropertySerializer` for GraphBinary. * Added date manipulation steps `asDate`, `dateAdd` and `dateDiff`. * Added new data type `DT` to represent periods of time. diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index c1825c261d..6cf71fda1e 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -730,7 +730,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#call-step[reference] *Description:* Concatenates the incoming String traverser with the input String arguments, and return the joined String. -*Syntax:* `concat()` | `concat(String...)` | `concat(Traversal)` +*Syntax:* `concat()` | `concat(String...)` | `concat(Traversal, Traversal...)` [width="100%",options="header"] |========================================================= @@ -740,10 +740,12 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#call-step[reference] *Arguments:* -* `String...` - If one or more String values are provided, they will be concatenated together with the -incoming traverser. If no argument is provided, the String value from the incoming traverser is returned. -* `Traversal` - The `Traversal` value must resolve to a `String`. The first result returned from the traversal will be -concatenated with the incoming traverser. +* `concatStrings` - Varargs of `String`. If one or more String values are provided, they will be concatenated together +with the incoming traverser. If no argument is provided, the String value from the incoming traverser is returned. +* `concatTraveral` - A `Traversal` whose must value resolve to a `String`. The first result returned from the traversal will +be concatenated with the incoming traverser. +* `otherConcatTraverals` - Varargs of `Traversal`. Each `Traversal` value must resolve to a `String`. The first result +returned from each traversal will be concatenated with the incoming traverser and the previous traversal arguments. Any `null` String values will be skipped when concatenated with non-`null` String values. If two `null` value are concatenated, the `null` value will be propagated and returned. diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index ee9f256d09..9278fef9be 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -1143,39 +1143,48 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre [[concat-step]] === Concat Step -The `concat()`-step (*map*) concatenates one or more String values together to the incoming String traverser. +The `concat()`-step (*map*) concatenates one or more String values together to the incoming String traverser. This step +can take either String varargs or Traversal varargs. +Any `null` String values will be skipped when concatenated with non-`null` String values. If two `null` value are +concatenated, the `null` value will be propagated and returned. If the incoming traverser is a non-String value then an `IllegalArgumentException` will be thrown. [gremlin-groovy,modern] ---- -g.addV(constant("prefix_").concat(__.V(1).label())).property(id, 10) <1> +g.addV(constant('prefix_').concat(__.V(1).label())).property(id, 10) <1> g.V(10).label() g.V().hasLabel('person').values('name').as('a'). constant('Mr.').concat(__.select('a')) <2> g.V().hasLabel('software').as('a').values('name'). concat(' uses '). concat(select('a').values('lang')) <3> -g.V(1).outE().as("a").constant(""). - concat(V(1).values("name")). - concat(" "). - concat(select("a").label()). - concat(" "). - concat(select("a").inV().values("name")) <4> -g.inject('hello', 'hi').concat(__.V().values('name')) <5> -g.inject('This').concat(' ').concat('is a ', 'gremlin.') <6> +g.V(1).outE().as('a').V(1).values('name'). + concat(' '). + concat(select('a').label()). + concat(' '). + concat(select("a").inV().values('name')) <4> +g.V(1).outE().as('a').V(1).values('name'). + concat(constant(' '), + select("a").label(), + constant(' '), + select('a').inV().values('name')) <5> +g.inject('hello', 'hi').concat(__.V().values('name')) <6> +g.inject('This').concat(' ').concat('is a ', 'gremlin.') <7> ---- <1> Add a new vertex with id 10 which should be labeled like an existing vertex but with some prefix attached <2> Attach the prefix "Mr." to all the names using the `constant()`-step <3> Generate a string of software names and the language they use <4> Generate a string description for each of marko's outgoing edges -<5> The `concat()` step will append the first result from the child traversal to the incoming traverser -<6> A generic use of `concat()` to join strings together +<5> Alternative way to generate the string description by using traversal varargs. Use the `constant()` step to add +desired strings between arguments. +<6> The `concat()` step will append the first result from the child traversal to the incoming traverser +<7> A generic use of `concat()` to join strings together *Additional References* -link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-String++[`concat(String...)`] -link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-Traversal++[`concat(Traversal)`] +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-java.lang.String-++[`concat(String...)`] +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat-org.apache.tinkerpop.gremlin.process.traversal.Traversal-org.apache.tinkerpop.gremlin.process.traversal.Traversal...-++[`concat(Taversal, Traversal...)`] link:++https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#concat-step++[`Semantics`] [[connectedcomponent-step]] diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc index 5b50aa07c8..a17158853d 100644 --- a/docs/src/upgrade/release-3.7.x.asciidoc +++ b/docs/src/upgrade/release-3.7.x.asciidoc @@ -30,7 +30,36 @@ 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. +Modifications to the concat() step as well as additional String manipulation steps have been added to this version. + +===== Updates to concat(): +Concat has been modified to take traversal varargs instead of a single traversal. Users no longer have to chain +concat() steps together to concatenate multiple traversals: +[source,text] +---- +gremlin> g.V(1).outE().as("a").V(1).values("name").concat(select("a").label(), select("a").inV().values("name")) +==>markocreatedlop +==>markoknowsvadas +==>markoknowsjosh +---- + +A notable breaking change from 3.7.0 is that we have output order of `inject()` as a child of `concat()` to be consistent with other parent steps. Any 3.7.0 uses of `concat(inject(X))` should change to `concat(constant(X))` to retain the old semantics. +[source,text] +---- +// 3.7.0 +gremlin> g.inject("a").concat(inject("b")) +==>ab + +// 3.7.1 +gremlin> g.inject("a").concat(inject()) +==>aa +gremlin> g.inject("a").concat(inject("b")) +==>aa +gremlin> g.inject("a").concat(constant("b")) +==>ab +---- + +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#concat-step[concat()-step], ===== String Steps asString(), length(), toLower(), toUpper(): 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 eca310f6aa..63aaaedd03 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 @@ -923,7 +923,7 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override - public T visitTraversalMethod_concat_Traversal(final GremlinParser.TraversalMethod_concat_TraversalContext ctx) { notImplemented(ctx); return null; } + public T visitTraversalMethod_concat_Traversal_Traversal(final GremlinParser.TraversalMethod_concat_Traversal_TraversalContext 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 dc42e84598..68a3b37307 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 @@ -1745,8 +1745,12 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> * {@inheritDoc} */ @Override - public GraphTraversal visitTraversalMethod_concat_Traversal(final GremlinParser.TraversalMethod_concat_TraversalContext ctx) { - return graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + public GraphTraversal visitTraversalMethod_concat_Traversal_Traversal(final GremlinParser.TraversalMethod_concat_Traversal_TraversalContext ctx) { + if (ctx.getChildCount() == 4) { + return this.graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + return this.graphTraversal.concat(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()), + antlr.tListVisitor.visitNestedTraversalList(ctx.nestedTraversalList())); } /** 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 a6257a4eba..48cd33a1dc 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 @@ -1432,21 +1432,24 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { } /** - * Concatenate strings. + * Concatenate values of an arbitrary number of string traversals to the incoming traverser. * * @return the traversal with an appended {@link ConcatStep}. + * @param concatTraversal the traversal to concatenate. + * @param otherConcatTraversals additional traversals to concatenate. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#concat-step" target="_blank">Reference Documentation - Concat Step</a> - * @since 3.7.0 + * @since 3.7.1 */ - public default GraphTraversal<S, String> concat(final Traversal<?, String> concatTraversal) { - this.asAdmin().getBytecode().addStep(Symbols.concat, concatTraversal); - return this.asAdmin().addStep(new ConcatStep<>(this.asAdmin(), concatTraversal)); + public default GraphTraversal<S, String> concat(final Traversal<?, String> concatTraversal, final Traversal<?, String>... otherConcatTraversals) { + this.asAdmin().getBytecode().addStep(Symbols.concat, concatTraversal, otherConcatTraversals); + return this.asAdmin().addStep(new ConcatStep<>(this.asAdmin(), concatTraversal, otherConcatTraversals)); } /** - * Concatenate strings. + * Concatenate an arbitrary number of strings to the incoming traverser. * * @return the traversal with an appended {@link ConcatStep}. + * @param concatStrings the String values to concatenate. * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#concat-step" target="_blank">Reference Documentation - Concat Step</a> * @since 3.7.0 */ 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 cee80f1dad..f9596b96f0 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 @@ -588,10 +588,10 @@ public class __ { } /** - * @see GraphTraversal#concat(Traversal) + * @see GraphTraversal#concat(Traversal, Traversal...) */ - public static <A> GraphTraversal<A, String> concat(final Traversal<?, String> concatTraversal) { - return __.<A>start().concat(concatTraversal); + public static <A> GraphTraversal<A, String> concat(final Traversal<A, String> concatTraversal, final Traversal<A, String>... otherConcatTraversals) { + return __.<A>start().concat(concatTraversal, otherConcatTraversals); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java index 982b5969eb..f473561d92 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java @@ -21,14 +21,17 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; -import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Reference implementation for String concatenation step, a mid-traversal step which concatenates one or more @@ -39,10 +42,10 @@ import java.util.Set; */ public final class ConcatStep<S> extends ScalarMapStep<S, String> implements TraversalParent { + private List<Traversal.Admin<S, String>> concatTraversals; + private List<String> concatStrings; private String stringArgsResult; - private Traversal.Admin<S, String> concatTraversal; - // flag used to propagate the null value through if all strings to be concatenated are null private boolean isNullTraverser = true; private boolean isNullTraversal = true; @@ -50,12 +53,15 @@ public final class ConcatStep<S> extends ScalarMapStep<S, String> implements Tra public ConcatStep(final Traversal.Admin traversal, final String... concatStrings) { super(traversal); + this.concatStrings = Collections.unmodifiableList(Arrays.asList(concatStrings)); this.stringArgsResult = processStrings(concatStrings); } - public ConcatStep(final Traversal.Admin traversal, final Traversal<S, String> concatTraversal) { + public ConcatStep(final Traversal.Admin traversal, final Traversal<?, String> concatTraversal, final Traversal<?, String>... otherConcatTraversals) { super(traversal); - this.concatTraversal = this.integrateChild(concatTraversal.asAdmin()); + this.concatTraversals = new ArrayList<>(Collections.singletonList((Traversal.Admin<S, String>) concatTraversal.asAdmin())); + this.concatTraversals.addAll(Stream.of(otherConcatTraversals).map(ct -> (Traversal.Admin<S, String>) ct.asAdmin()).collect(Collectors.toList())); + this.concatTraversals.forEach(this::integrateChild); } @Override @@ -74,18 +80,14 @@ public final class ConcatStep<S> extends ScalarMapStep<S, String> implements Tra sb.append(traverser.get()); } - if (null != this.concatTraversal) { - if (this.concatTraversal.getStartStep() instanceof InjectStep) { // inject as start step is processed separately - if (this.concatTraversal.hasNext()) { - final String result = this.concatTraversal.next(); - if (null != result) { - this.isNullTraversal = false; - sb.append(result); - } + if (null != this.concatTraversals) { + this.concatTraversals.forEach(ct -> { + final String result = TraversalUtil.apply(traverser, ct); + if (null != result) { + this.isNullTraversal = false; + sb.append(result); } - } else { - sb.append(TraversalUtil.apply(traverser, this.concatTraversal)); - } + }); } if (!this.isNullString) { @@ -117,40 +119,59 @@ public final class ConcatStep<S> extends ScalarMapStep<S, String> implements Tra @Override public Set<TraverserRequirement> getRequirements() { - return Collections.singleton(TraverserRequirement.OBJECT); + return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT); } @Override public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) { super.setTraversal(parentTraversal); - this.integrateChild(this.concatTraversal); + if (null != this.concatTraversals) + for (final Traversal.Admin<S, String> traversal : this.concatTraversals) { + this.integrateChild(traversal); + } } @Override public ConcatStep<S> clone() { final ConcatStep<S> clone = (ConcatStep<S>) super.clone(); - if (null != this.concatTraversal) - clone.concatTraversal = this.concatTraversal.clone(); + if (null != this.concatTraversals) { + clone.concatTraversals = new ArrayList<>(); + for (final Traversal.Admin<S, String> concatTraversals : this.concatTraversals) { + clone.concatTraversals.add(concatTraversals.clone()); + } + } + if (null != this.concatStrings) { + clone.concatStrings = new ArrayList<>(); + clone.concatStrings.addAll(this.concatStrings); + } return clone; } @Override public List<Traversal.Admin<S, String>> getLocalChildren() { - return null == this.concatTraversal ? Collections.emptyList() : Collections.singletonList(this.concatTraversal); + return null == this.concatTraversals ? Collections.emptyList() : this.concatTraversals; } @Override public String toString() { - return StringFactory.stepString(this, this.concatTraversal); + if (null != this.concatTraversals) + return StringFactory.stepString(this, this.concatTraversals); + return StringFactory.stepString(this, this.concatStrings); } @Override public int hashCode() { int result = super.hashCode(); - if (null != this.concatTraversal) - result = super.hashCode() ^ this.concatTraversal.hashCode(); - if (null != this.stringArgsResult) - result = super.hashCode() ^ this.stringArgsResult.hashCode(); + if (null != this.concatTraversals) { + for (final Traversal t : this.concatTraversals) { + result = 31 * result + (null != t ? t.hashCode() : 0); + } + } + if (null != this.concatStrings) { + for (final String s : this.concatStrings) { + result = 31 * result + (null != s ? s.hashCode() : 0); + } + } return result; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java index 017e774c39..5421b45b76 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java @@ -528,7 +528,6 @@ public final class DotNetTranslator implements Translator.ScriptTranslator { TO_CS_MAP.put(GraphTraversal.Symbols.cap, "Cap<object>"); TO_CS_MAP.put(GraphTraversal.Symbols.choose, "Choose<object>"); TO_CS_MAP.put(GraphTraversal.Symbols.coalesce, "Coalesce<object>"); - TO_CS_MAP.put(GraphTraversal.Symbols.concat, "Concat<object>"); TO_CS_MAP.put(GraphTraversal.Symbols.constant, "Constant<object>"); TO_CS_MAP.put(GraphTraversal.Symbols.elementMap, "ElementMap<object>"); TO_CS_MAP.put(GraphTraversal.Symbols.flatMap, "FlatMap<object>"); 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 a4c4ddd33f..17a4d0522c 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 @@ -1153,6 +1153,34 @@ public class TraversalMethodVisitorTest { eval("g.V().coalesce(__.E(1,2),__.addE('person'))")); } + @Test + public void shouldParseTraversalMethod_concat_Empty() throws Exception { + compare(g.V().concat(), eval("g.V().concat()")); + } + + @Test + public void shouldParseTraversalMethod_concat_multipleStringArgs() throws Exception { + compare(g.V().concat("hello", "world"), eval("g.V().concat('hello', 'world')")); + } + + @Test + public void shouldParseTraversalMethod_concat_traversal() throws Exception { + compare(g.V().concat(constant("hello")), + eval("g.V().concat(__.constant('hello'))")); + } + + @Test + public void shouldParseTraversalMethod_concat_multipleTraversalArgs() throws Exception { + compare(g.V().concat(constant("hello"), constant("world")), + eval("g.V().concat(__.constant('hello'), __.constant('world'))")); + } + + @Test + public void shouldParseTraversalMethod_concat_ArgsWithNulls() throws Exception { + compare(g.V().concat(null, "hello"), + eval("g.V().concat(null, 'hello')")); + } + @Test public void shouldParseTraversalMethod_asString_Empty() throws Exception { compare(g.V().asString(), eval("g.V().asString()")); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java index 0c17deac69..4229aa6418 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStepTest.java @@ -40,20 +40,24 @@ public class ConcatStepTest extends StepTest { @Test public void testReturnTypes() { + assertEquals("a", __.__("a").concat().next()); assertEquals("abc", __.__("a").concat("b", "c").next()); - assertEquals("ab", __.__("a").concat(__.inject("b", "c")).next()); - assertEquals("ab", __.__("a").concat(__.inject(Arrays.asList("b", "c")).unfold()).next()); + assertEquals("aa", __.__("a").concat(__.inject("b", "c")).next()); + assertEquals("aa", __.__("a").concat(__.inject(Arrays.asList("b", "c")).unfold()).next()); assertEquals("", __.__("").concat("").next()); assertArrayEquals(new String[]{"a", "b", "c"}, __.__("a", "b", "c").concat().toList().toArray()); assertArrayEquals(new String[]{"ade", "bde", "cde"}, __.__("a", "b", "c").concat("d", "e").toList().toArray()); - assertArrayEquals(new String[]{"ad", "be", "c"}, + assertArrayEquals(new String[]{"aa", "bb", "cc"}, __.__("a", "b", "c").concat(__.inject("d", "e")).toList().toArray()); assertArrayEquals(new String[]{"Mr.a", "Mr.b", "Mr.c", "Mr.d"}, __.__("a", "b", "c", "d").as("letters").constant("Mr.").concat(__.select("letters")).toList().toArray()); + assertArrayEquals(new String[]{"Hello Mr.a", "Hello Mr.b", "Hello Mr.c", "Hello Mr.d"}, + __.__("a", "b", "c", "d").as("letters"). + constant("Hello ").concat(__.constant("Mr."), __.select("letters")).toList().toArray()); String nullStr = null; assertNull(__.inject(null).concat(nullStr).next()); diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index d232bff33d..de7cc131ea 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -541,23 +541,33 @@ namespace Gremlin.Net.Process.Traversal /// <summary> /// Adds the concat step to this <see cref="GraphTraversal{SType, EType}" />. /// </summary> - public GraphTraversal<TStart, TEnd> Concat<TNewEnd> (ITraversal concatTraversal) + public GraphTraversal<TStart, string> Concat (ITraversal concatTraversal, params ITraversal[]? otherConcatTraversals) { - Bytecode.AddStep("concat", concatTraversal); - return Wrap<TStart, TEnd>(this); + List<object?> args; + if (otherConcatTraversals == null) + { + args = new List<object?> { concatTraversal }; + } + else + { + args = new List<object?>(1 + otherConcatTraversals.Length) { concatTraversal }; + args.AddRange(otherConcatTraversals); + } + Bytecode.AddStep("concat", args.ToArray()); + return Wrap<TStart, string>(this); } /// <summary> /// Adds the concat step to this <see cref="GraphTraversal{SType, EType}" />. /// </summary> - public GraphTraversal<TStart, TEnd> Concat<TNewEnd> (params string?[] concatStrings) + public GraphTraversal<TStart, string> Concat (params string?[] concatStrings) { // need null check? var args = new List<object?>(concatStrings.Length); args.AddRange(concatStrings); Bytecode.AddStep("concat", args.ToArray()); - return Wrap<TStart, TEnd>(this); + return Wrap<TStart, string>(this); } /// <summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs index 070644c89d..7bda3700e7 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs @@ -347,19 +347,19 @@ namespace Gremlin.Net.Process.Traversal /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the concat step to that traversal. /// </summary> - public static GraphTraversal<object, object> Concat<E2> (ITraversal concatTraversal) + public static GraphTraversal<object, string> Concat (ITraversal concatTraversal, ITraversal[]? otherConcatTraversals) { - return new GraphTraversal<object, object>().Concat<E2>(concatTraversal); + return new GraphTraversal<object, string>().Concat(concatTraversal, otherConcatTraversals); } /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the concat step to that traversal. /// </summary> - public static GraphTraversal<object, object> Concat<E2>(params string?[] concatStrings) + public static GraphTraversal<object, string> Concat(params string?[] concatStrings) { return concatStrings is { Length: 0 } - ? new GraphTraversal<object, object>().Concat<E2>() - : new GraphTraversal<object, object>().Concat<E2>(concatStrings); + ? new GraphTraversal<object, string>().Concat() + : new GraphTraversal<object, string>().Concat(concatStrings); } /// <summary> diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 718d60d0f9..f2cbf513b0 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -537,7 +537,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"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_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(" 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")}}, @@ -564,20 +564,21 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_coalesceXoutXlikesX_outXknowsX_inXcreatedXX_groupCount_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coalesce<object>(__.Out("likes"),__.Out("knows"),__.Out("created")).GroupCount<object>().By("name")}}, {"g_V_coalesceXoutEXknowsX_outEXcreatedXX_otherV_path_byXnameX_byXlabelX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coalesce<object>(__.OutE("knows"),__.OutE("created")).OtherV().Path().By("name").By(T.Label)}}, {"g_V_outXcreatedX_order_byXnameX_coalesceXname_constantXxXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").Order().By("name").Coalesce<object>(__.Values<object>("name"),__.Constant<object>("x"))}}, - {"g_injectXa_bX_concat", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat<object>()}}, - {"g_injectXa_bX_concat_XcX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat<object>("c")}}, - {"g_injectXa_bX_concat_Xc_dX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat<object>("c","d")}}, - {"g_injectXa_bX_concat_Xinject_c_dX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat<object>(__.Inject("c","d"))}}, - {"g_injectXListXa_bXcX_concat_XdX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"],"c").Concat<object>("d")}}, - {"g_injectXaX_concat_Xinject_List_b_cX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a").Concat<object>(__.Inject(p["xx1"]))}}, - {"g_injectXnullX_concat_XinjectX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).Concat<object>()}}, - {"g_injectXnull_aX_concat_Xnull_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,"a").Concat<object>(null,"b")}}, - {"g_injectXhello_hiX_concat_XV_valuesXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("hello","hi").Concat<object>(__.V().Order().By(T.Id).Values<object>("name"))}}, - {"g_V_hasLabel_value_concat_X_X_concat_XpersonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").Concat<object>(" ").Concat<object>("person")}}, - {"g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").As("a").Constant<object>("Mr.").Concat<object>(__.Select<object>("a"))}}, - {"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").As("a").Values<object>("name").Concat<object>(" uses ").Concat<object>(__.Select<object>("a").Values<object>("lang"))}}, - {"g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat<object>(__.Select<object>("a").Label()).Concat<object>(__.Select<object>("a").InV().Values<object>("name"))}}, - {"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", 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").Proper [...] + {"g_injectXa_bX_concat", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat()}}, + {"g_injectXa_bX_concat_XcX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat("c")}}, + {"g_injectXa_bX_concat_Xc_dX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat("c","d")}}, + {"g_injectXa_bX_concat_Xinject_c_dX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a","b").Concat(__.Inject("c"))}}, + {"g_injectXaX_concat_Xinject_List_b_cX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("a").Concat(__.Inject(p["xx1"]))}}, + {"g_injectXListXa_bXcX_concat_XdX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"],"c").Concat("d")}}, + {"g_injectXnullX_concat_XinjectX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).Concat()}}, + {"g_injectXnull_aX_concat_Xnull_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,"a").Concat(null,"b")}}, + {"g_injectXhello_hiX_concat_XV_valuesXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject("hello","hi").Concat(__.V().Order().By(T.Id).Values<object>("name"))}}, + {"g_V_hasLabel_value_concat_X_X_concat_XpersonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").Concat(" ").Concat("person")}}, + {"g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").As("a").Constant<object>("Mr.").Concat(__.Select<object>("a"))}}, + {"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").As("a").Values<object>("name").Concat(" uses ").Concat(__.Select<object>("a").Values<object>("lang"))}}, + {"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label()).Concat(__.Select<object>("a").InV().Values<object>("name"))}}, + {"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label(),__.Select<object>("a").InV().Values<object>("name"))}}, + {"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", 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").Proper [...] {"g_V_connectedComponent_hasXcomponentX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}}, {"g_V_dedup_connectedComponent_hasXcomponentX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Dedup().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}}, {"g_V_hasLabelXsoftwareX_connectedComponent_project_byXnameX_byXcomponentX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").ConnectedComponent().Project<object>("name","component").By("name").By("gremlin.connectedComponentVertexProgram.component")}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index b78f9629c0..fcc5e3a4c8 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -538,16 +538,17 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_injectXa_bX_concat": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", "b").Concat()}}, "g_injectXa_bX_concat_XcX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", "b").Concat("c")}}, "g_injectXa_bX_concat_Xc_dX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", "b").Concat("c", "d")}}, - "g_injectXa_bX_concat_Xinject_c_dX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", "b").Concat(gremlingo.T__.Inject("c", "d"))}}, - "g_injectXListXa_bXcX_concat_XdX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"], "c").Concat("d")}}, + "g_injectXa_bX_concat_Xinject_c_dX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a", "b").Concat(gremlingo.T__.Inject("c"))}}, "g_injectXaX_concat_Xinject_List_b_cX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("a").Concat(gremlingo.T__.Inject(p["xx1"]))}}, + "g_injectXListXa_bXcX_concat_XdX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"], "c").Concat("d")}}, "g_injectXnullX_concat_XinjectX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil).Concat()}}, "g_injectXnull_aX_concat_Xnull_bX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, "a").Concat(nil, "b")}}, "g_injectXhello_hiX_concat_XV_valuesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("hello", "hi").Concat(gremlingo.T__.V().Order().By(gremlingo.T.Id).Values("name"))}}, "g_V_hasLabel_value_concat_X_X_concat_XpersonX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Concat(" ").Concat("person")}}, "g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").As("a").Constant("Mr.").Concat(gremlingo.T__.Select("a"))}}, "g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").As("a").Values("name").Concat(" uses ").Concat(gremlingo.T__.Select("a").Values("lang"))}}, - "g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label()).Concat(gremlingo.T__.Select("a").InV().Values("name"))}}, + "g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label()).Concat(gremlingo.T__.Select("a").InV().Values("name"))}}, + "g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values("name").Concat(gremlingo.T__.Select("a").Label(), gremlingo.T__.Select("a").InV().Values("name"))}}, "g_addVXconstantXprefix_X_concatXVX1X_labelX_label": {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", "ripp [...] "g_V_connectedComponent_hasXcomponentX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}}, "g_V_dedup_connectedComponent_hasXcomponentX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Dedup().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}}, 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 2fff0455c8..3750ae85e4 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 @@ -558,16 +558,17 @@ const gremlins = { g_injectXa_bX_concat: [function({g}) { return g.inject("a","b").concat() }], g_injectXa_bX_concat_XcX: [function({g}) { return g.inject("a","b").concat("c") }], g_injectXa_bX_concat_Xc_dX: [function({g}) { return g.inject("a","b").concat("c","d") }], - g_injectXa_bX_concat_Xinject_c_dX: [function({g}) { return g.inject("a","b").concat(__.inject("c","d")) }], - g_injectXListXa_bXcX_concat_XdX: [function({g, xx1}) { return g.inject(xx1,"c").concat("d") }], + g_injectXa_bX_concat_Xinject_c_dX: [function({g}) { return g.inject("a","b").concat(__.inject("c")) }], g_injectXaX_concat_Xinject_List_b_cX: [function({g, xx1}) { return g.inject("a").concat(__.inject(xx1)) }], + g_injectXListXa_bXcX_concat_XdX: [function({g, xx1}) { return g.inject(xx1,"c").concat("d") }], g_injectXnullX_concat_XinjectX: [function({g}) { return g.inject(null).concat() }], g_injectXnull_aX_concat_Xnull_bX: [function({g}) { return g.inject(null,"a").concat(null,"b") }], g_injectXhello_hiX_concat_XV_valuesXnameXX: [function({g}) { return g.inject("hello","hi").concat(__.V().order().by(T.id).values("name")) }], g_V_hasLabel_value_concat_X_X_concat_XpersonX: [function({g}) { return g.V().hasLabel("person").values("name").concat(" ").concat("person") }], g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX: [function({g}) { return g.V().hasLabel("person").values("name").as("a").constant("Mr.").concat(__.select("a")) }], g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX: [function({g}) { return g.V().hasLabel("software").as("a").values("name").concat(" uses ").concat(__.select("a").values("lang")) }], - g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX: [function({g, vid1}) { return g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label()).concat(__.select("a").inV().values("name")) }], + g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX: [function({g, vid1}) { return g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label()).concat(__.select("a").inV().values("name")) }], + g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX: [function({g, vid1}) { return g.V(vid1).outE().as("a").V(vid1).values("name").concat(__.select("a").label(),__.select("a").inV().values("name")) }], g_addVXconstantXprefix_X_concatXVX1X_labelX_label: [function({g, vid1}) { 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"). [...] g_V_connectedComponent_hasXcomponentX: [function({g}) { return g.V().connectedComponent().has("gremlin.connectedComponentVertexProgram.component") }], g_V_dedup_connectedComponent_hasXcomponentX: [function({g}) { return g.V().dedup().connectedComponent().has("gremlin.connectedComponentVertexProgram.component") }], diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index fe38cd5567..13f4384232 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -830,7 +830,7 @@ traversalMethod_call ; traversalMethod_concat - : 'concat' LPAREN nestedTraversal RPAREN #traversalMethod_concat_Traversal + : 'concat' LPAREN nestedTraversal (COMMA nestedTraversalList)? RPAREN #traversalMethod_concat_Traversal_Traversal | 'concat' LPAREN stringLiteralVarargs RPAREN #traversalMethod_concat_String ; diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 71665597ae..915535ece3 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -540,16 +540,17 @@ world.gremlins = { 'g_injectXa_bX_concat': [(lambda g:g.inject('a','b').concat())], 'g_injectXa_bX_concat_XcX': [(lambda g:g.inject('a','b').concat('c'))], 'g_injectXa_bX_concat_Xc_dX': [(lambda g:g.inject('a','b').concat('c','d'))], - 'g_injectXa_bX_concat_Xinject_c_dX': [(lambda g:g.inject('a','b').concat(__.inject('c','d')))], - 'g_injectXListXa_bXcX_concat_XdX': [(lambda g, xx1=None:g.inject(xx1,'c').concat('d'))], + 'g_injectXa_bX_concat_Xinject_c_dX': [(lambda g:g.inject('a','b').concat(__.inject('c')))], 'g_injectXaX_concat_Xinject_List_b_cX': [(lambda g, xx1=None:g.inject('a').concat(__.inject(xx1)))], + 'g_injectXListXa_bXcX_concat_XdX': [(lambda g, xx1=None:g.inject(xx1,'c').concat('d'))], 'g_injectXnullX_concat_XinjectX': [(lambda g:g.inject(None).concat())], 'g_injectXnull_aX_concat_Xnull_bX': [(lambda g:g.inject(None,'a').concat(None,'b'))], 'g_injectXhello_hiX_concat_XV_valuesXnameXX': [(lambda g:g.inject('hello','hi').concat(__.V().order().by(T.id_).name))], 'g_V_hasLabel_value_concat_X_X_concat_XpersonX': [(lambda g:g.V().hasLabel('person').name.concat(' ').concat('person'))], 'g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX': [(lambda g:g.V().hasLabel('person').name.as_('a').constant('Mr.').concat(__.select('a')))], 'g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX': [(lambda g:g.V().hasLabel('software').as_('a').name.concat(' uses ').concat(__.select('a').lang))], - 'g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX': [(lambda g, vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label()).concat(__.select('a').in_v().name))], + 'g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX': [(lambda g, vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label()).concat(__.select('a').in_v().name))], + 'g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX': [(lambda g, vid1=None:g.V(vid1).outE().as_('a').V(vid1).name.concat(__.select('a').label(),__.select('a').in_v().name))], 'g_addVXconstantXprefix_X_concatXVX1X_labelX_label': [(lambda g, vid1=None: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').p [...] 'g_V_connectedComponent_hasXcomponentX': [(lambda g:g.V().connectedComponent().has('gremlin.connectedComponentVertexProgram.component'))], 'g_V_dedup_connectedComponent_hasXcomponentX': [(lambda g:g.V().dedup().connectedComponent().has('gremlin.connectedComponentVertexProgram.component'))], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature index bced98cdad..c9a0e717cc 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Concat.feature @@ -62,35 +62,37 @@ Feature: Step - concat() Given the empty graph And the traversal of """ - g.inject("a", "b").concat(__.inject("c", "d")) + g.inject("a", "b").concat(__.inject("c")) """ When iterated to list Then the result should be unordered | result | - | ac | - | bd | + | aa | + | bb | @GraphComputerVerificationInjectionNotSupported - Scenario: g_injectXListXa_bXcX_concat_XdX + Scenario: g_injectXaX_concat_Xinject_List_b_cX Given the empty graph - And using the parameter xx1 defined as "l[a,b]" + And using the parameter xx1 defined as "l[b,c]" And the traversal of """ - g.inject(xx1,"c").concat("d") + g.inject("a").concat(__.inject(xx1)) """ When iterated to list - Then the traversal will raise an error with message containing text of "String concat() can only take string as argument" + Then the result should be unordered + | result | + | aa | @GraphComputerVerificationInjectionNotSupported - Scenario: g_injectXaX_concat_Xinject_List_b_cX + Scenario: g_injectXListXa_bXcX_concat_XdX Given the empty graph - And using the parameter xx1 defined as "l[b,c]" + And using the parameter xx1 defined as "l[a,b]" And the traversal of """ - g.inject("a").concat(__.inject(xx1)) + g.inject(xx1,"c").concat("d") """ When iterated to list - Then the traversal will raise an error + Then the traversal will raise an error with message containing text of "String concat() can only take string as argument" @GraphComputerVerificationInjectionNotSupported Scenario: g_injectXnullX_concat_XinjectX @@ -172,7 +174,7 @@ Feature: Step - concat() | ripple uses java | @GraphComputerVerificationMidVNotSupported - Scenario: g_VX1X_outE_asXaX_constantXX_concatXVX1X_valuesXnameX_concatXselectXaX_label_concatXselectXaX_inV_valuesXnameX + Scenario: g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX Given the modern graph And using the parameter vid1 defined as "v[marko].id" And the traversal of @@ -186,6 +188,21 @@ Feature: Step - concat() | markoknowsvadas | | markoknowsjosh | + @GraphComputerVerificationMidVNotSupported + Scenario: g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).outE().as("a").V(vid1).values("name").concat(select("a").label(), select("a").inV().values("name")) + """ + When iterated to list + Then the result should be unordered + | result | + | markocreatedlop | + | markoknowsvadas | + | markoknowsjosh | + Scenario: g_addVXconstantXprefix_X_concatXVX1X_labelX_label Given the empty graph And the graph initializer of