You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by xi...@apache.org on 2023/10/23 23:09:54 UTC
[tinkerpop] branch master updated: update concat() to accept traversal varargs and not treat inject as special case in argument (#2306)
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 <55...@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