Skip to content

Commit 0b2e77e

Browse files
Remove GenericArgument from from() and to() (#3252)
from() and to() will now only accept Strings (alias for __.select()) and Traversals. If users wish to pass a vertex id, they may embed it in __.V() or __.constant(). The primary motivation here is to ensure consistent behaviour accross providers regardless of if their id types are numerics, strings, or other. This change is a followup to an unintended consequence stemming from #3133. The issue with directly allowing ID's to be passed into from/to, it creates a weird asymmetry for graphs which use Strings as their ids. For example g.addE().from(1).to(2) would work fine and create an edge from V[1] to V[2], however if the graph used String ids, the equivalent traversal (g.addE().from("a").to("b")) does not work, as the Strings are interpreted as path labels (converted to __.select("a")) instead of vertex ids. There are some ugly workarounds which can partially mitigate this issue, but overall there is no generalized fix which allows all traversals to behave the same regardless of the vertex id type. --------- Co-authored-by: andreachild <andrea.child@improving.com>
1 parent 54c4271 commit 0b2e77e

17 files changed

Lines changed: 79 additions & 140 deletions

File tree

docs/src/dev/provider/gremlin-semantics.asciidoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,12 @@ The `addE()` step can be used as both a start step and a mid-traversal step. Whe
535535
and `to()` must be specified. When used as a mid-traversal step, the current traverser becomes the source vertex and
536536
only `to()` needs to be specified.
537537
538+
The gremlin-lang grammar only permits `Traversal` and `String` (alias for `__.select(String)`) arguments in `from()` and
539+
`to()`. The `Traversal` must either produce a `Vertex` which is attachable to the graph, or it must produce the id of an
540+
existing `Vertex` in the graph. `GraphTraversal` implementations in GLVs may optionally support `from(Vertex)` and
541+
`to(Vertex)` as syntactic sugar. If translating to gremlin-lang scripts, these sugared modulators must be converted to
542+
`from(__.V(vertex.id()))` or `from(__.constant(vertex.id()))` (and equivalents for `to()`).
543+
538544
*Exceptions*
539545
540546
* If the edge label is null, an `IllegalArgumentException` will be thrown.

docs/src/reference/the-traversal.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ vMarko = g.V().has('name','marko').next()
553553
vPeter = g.V().has('name','peter').next()
554554
g.V(vMarko).addE('knows').to(vPeter) <6>
555555
g.addE('knows').from(vMarko).to(vPeter) <7>
556+
g.addE('knows').from(__.V(1)).to(__.constant(6)) <8>
556557
----
557558
558559
<1> Add a co-developer edge with a year-property between marko and his collaborators.
@@ -563,6 +564,7 @@ g.addE('knows').from(vMarko).to(vPeter) <7>
563564
supports user provided ids.
564565
<6> Add an edge between marko and peter given the directed (detached) vertex references.
565566
<7> Add an edge between marko and peter given the directed (detached) vertex references.
567+
<8> Use child traversals producing either a vertex, or vertex id to add an edge between marko and peter.
566568
567569
*Additional References*
568570

docs/src/upgrade/release-3.8.x.asciidoc

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -737,41 +737,44 @@ after construction. All usages of `P.getOriginalValue()` should be replaced with
737737
738738
A number of changes have been introduced to the Gremlin grammar to help make it be more consistent and easier to use.
739739
740-
*Removed Vertex References for Grammar*
740+
*Removed StructureVertex from Grammar*
741741
742742
The grammar allowed the construction of a `Vertex` by way of syntax like `new Vertex(1,'person')` (or with similar
743743
arguments to `ReferenceVertex`). This syntax has been removed as it served little purpose within the grammar as it
744744
merely adds more characters to wrap around the identifier, which could simply be used by itself.
745745
746746
The `V()` step, as well as the `from()` and `to()` modulators used with `addE()`, previously accepted `Vertex` as
747-
arguments in the grammar. In its place, the `from()` and `to()` modulators can now directly accept a vertex id in place
748-
of a `Vertex` when used with `addE()` (`V()` has always accepted ids in addition to vertices). When using these steps in
749-
`gremlin-lang` scripts, the vertex id must be used directly.
747+
arguments in the grammar. The `V()` step has always accepted vertex ids as arguments, and continues to do so. The
748+
`from()` and `to()` modulators for `addE()` continue to accept `String` arguments (which are a shorthand for
749+
`__.select(String)`), as well as `Traversal` arguments. As always, these `Traversal` arguments may produce `Vertex`
750+
objects (such as `__.V(1)`) to directly bind to from/to. Newly added in 3.8.0, the `Traversal` may also produce the id
751+
of a vertex present in the graph (such as `__.constant(1)`), which will then bind to from/to.
750752
751-
This change has no effect on the `GraphTraversal` API, nor on `gremlin-groovy` scripts. Vertices can continue to be used
752-
directly in those contexts.
753+
When using these steps in `gremlin-lang` scripts, a `Traversal` or `String` argument must be used directly. This change
754+
has no effect on the `GraphTraversal` API, nor on `gremlin-groovy` scripts. Vertices can continue to be used directly in
755+
those contexts.
753756
754757
[source,text]
755758
----
756-
// 3.7.3
759+
// 3.7.4
757760
gremlin> v1 = g.V(1).next()
758761
==>v[1]
759-
gremlin> v2 = g.V(2).next()
760-
==>v[2]
761-
gremlin> script = String.format("g.V(new Vertex(%s)).outE().where(inV().is(new Vertex(%s)))", v1.id(), v2.id())
762-
==>g.V(new Vertex(1)).outE().where(inV().is(new Vertex(2)))
762+
gremlin> v6 = g.V(6).next()
763+
==>v[6]
764+
gremlin> script = String.format("g.addE('knows').from(new Vertex(%s)).to(new ReferenceVertex(%s))", v1.id(), v6.id())
765+
==>g.addE('knows').from(new Vertex(1)).to(new ReferenceVertex(6))
763766
gremlin> client.submit(script).all().get().get(0).getEdge()
764-
==>e[7][1-knows->2]
767+
==>e[0][1-knows->6]
765768
766769
// 3.8.0
767770
gremlin> v1 = g.V(1).next()
768771
==>v[1]
769-
gremlin> v2 = g.V(2).next()
770-
==>v[2]
771-
gremlin> script = String.format("g.V(%s).outE().where(inV().id().is(%s))", v1.id(), v2.id())
772-
==>g.V(1).outE().where(inV().id().is(2))
772+
gremlin> v6 = g.V(6).next()
773+
==>v[6]
774+
gremlin> script = String.format("g.addE('knows').from(__.V(%s)).to(__.constant(%s))", v1.id(), v6.id())
775+
==>g.addE('knows').from(__.V(1)).to(__.constant(6))
773776
gremlin> client.submit(script).all().get().get(0).getEdge()
774-
==>e[7][1-knows->2]
777+
==>e[0][1-knows->6]
775778
----
776779
777780
*`new` keyword is now optional*

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,6 @@ protected void notImplemented(final ParseTree ctx) {
403403
* {@inheritDoc}
404404
*/
405405
@Override public T visitTraversalMethod_from_String(final GremlinParser.TraversalMethod_from_StringContext ctx) { notImplemented(ctx); return null; }
406-
/**
407-
* {@inheritDoc}
408-
*/
409-
@Override public T visitTraversalMethod_from_GenricArgument(final GremlinParser.TraversalMethod_from_GenricArgumentContext ctx) { return null; }
410406
/**
411407
* {@inheritDoc}
412408
*/
@@ -839,10 +835,6 @@ protected void notImplemented(final ParseTree ctx) {
839835
* {@inheritDoc}
840836
*/
841837
@Override public T visitTraversalMethod_to_String(final GremlinParser.TraversalMethod_to_StringContext ctx) { notImplemented(ctx); return null; }
842-
/**
843-
* {@inheritDoc}
844-
*/
845-
@Override public T visitTraversalMethod_to_GenricArgument(final GremlinParser.TraversalMethod_to_GenricArgumentContext ctx) { notImplemented(ctx); return null; }
846838
/**
847839
* {@inheritDoc}
848840
*/

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,22 +1865,6 @@ public Traversal visitTraversalMethod_option_Predicate_Traversal(final GremlinPa
18651865
antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
18661866
}
18671867

1868-
/**
1869-
* {@inheritDoc}
1870-
*/
1871-
@Override
1872-
public GraphTraversal visitTraversalMethod_from_GenricArgument(final GremlinParser.TraversalMethod_from_GenricArgumentContext ctx) {
1873-
return graphTraversal.from(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
1874-
}
1875-
1876-
/**
1877-
* {@inheritDoc}
1878-
*/
1879-
@Override
1880-
public Traversal visitTraversalMethod_to_GenricArgument(final GremlinParser.TraversalMethod_to_GenricArgumentContext ctx) {
1881-
return graphTraversal.to(antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()));
1882-
}
1883-
18841868
/**
18851869
* {@inheritDoc}
18861870
*/

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,14 +1603,14 @@ public default GraphTraversal<S, E> from(final String fromStepLabel) {
16031603
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
16041604
* @since 3.8.0
16051605
*/
1606-
public default GraphTraversal<S, E> from(final GValue<Vertex> fromVertex) {
1606+
public default GraphTraversal<S, E> from(final GValue<?> fromVertex) {
16071607
final Step<?,?> prev = this.asAdmin().getEndStep();
16081608
if (!(prev instanceof FromToModulating))
16091609
throw new IllegalArgumentException(String.format(
16101610
"The from() step cannot follow %s", prev.getClass().getSimpleName()));
16111611

16121612
this.asAdmin().getBytecode().addStep(Symbols.from, fromVertex);
1613-
((FromToModulating) prev).addFrom(new GValueConstantTraversal<S, Vertex>(fromVertex));
1613+
((FromToModulating) prev).addFrom(new GValueConstantTraversal<>(fromVertex));
16141614
return this;
16151615
}
16161616

@@ -1662,14 +1662,14 @@ public default GraphTraversal<S, E> to(final String toStepLabel) {
16621662
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
16631663
* @since 3.8.0
16641664
*/
1665-
public default GraphTraversal<S, E> to(final GValue<Vertex> toVertex) {
1665+
public default GraphTraversal<S, E> to(final GValue<?> toVertex) {
16661666
final Step<?,?> prev = this.asAdmin().getEndStep();
16671667
if (!(prev instanceof FromToModulating))
16681668
throw new IllegalArgumentException(String.format(
16691669
"The to() step cannot follow %s", prev.getClass().getSimpleName()));
16701670

16711671
this.asAdmin().getBytecode().addStep(Symbols.to, toVertex);
1672-
((FromToModulating) prev).addTo(new GValueConstantTraversal<S, Vertex>(toVertex));
1672+
((FromToModulating) prev).addTo(new GValueConstantTraversal<>(toVertex));
16731673
return this;
16741674
}
16751675

@@ -1682,7 +1682,7 @@ public default GraphTraversal<S, E> to(final GValue<Vertex> toVertex) {
16821682
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
16831683
* @since 3.1.0-incubating
16841684
*/
1685-
public default GraphTraversal<S, E> to(final Traversal<?, Object> toVertex) {
1685+
public default GraphTraversal<S, E> to(final Traversal<?, ?> toVertex) {
16861686
final Step<?,?> prev = this.asAdmin().getEndStep();
16871687
if (!(prev instanceof FromToModulating))
16881688
throw new IllegalArgumentException(String.format(
@@ -1702,7 +1702,7 @@ public default GraphTraversal<S, E> to(final Traversal<?, Object> toVertex) {
17021702
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
17031703
* @since 3.1.0-incubating
17041704
*/
1705-
public default GraphTraversal<S, E> from(final Traversal<?, Object> fromVertex) {
1705+
public default GraphTraversal<S, E> from(final Traversal<?, ?> fromVertex) {
17061706
final Step<?,?> prev = this.asAdmin().getEndStep();
17071707
if (!(prev instanceof FromToModulating))
17081708
throw new IllegalArgumentException(String.format(
@@ -1722,13 +1722,7 @@ public default GraphTraversal<S, E> from(final Traversal<?, Object> fromVertex)
17221722
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
17231723
* @since 3.3.0
17241724
*/
1725-
public default GraphTraversal<S, E> to(final Object toVertex) {
1726-
if (toVertex instanceof String) {
1727-
return this.to((String) toVertex);
1728-
} else if (toVertex instanceof Traversal) {
1729-
this.to((Traversal<?, Object>)toVertex);
1730-
return this;
1731-
}
1725+
public default GraphTraversal<S, E> to(final Vertex toVertex) {
17321726
final Step<?,?> prev = this.asAdmin().getEndStep();
17331727
if (!(prev instanceof FromToModulating))
17341728
throw new IllegalArgumentException(String.format(
@@ -1741,34 +1735,6 @@ public default GraphTraversal<S, E> to(final Object toVertex) {
17411735
return this;
17421736
}
17431737

1744-
/**
1745-
* When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the
1746-
* outgoing vertex of the newly added {@link Edge}.
1747-
*
1748-
* @param fromVertex the vertex for selecting the outgoing vertex
1749-
* @return the traversal with the modified {@link AddEdgeStepContract}
1750-
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a>
1751-
* @since 3.3.0
1752-
*/
1753-
public default GraphTraversal<S, E> from(final Object fromVertex) {
1754-
if (fromVertex instanceof String) {
1755-
return this.from((String) fromVertex);
1756-
} else if (fromVertex instanceof Traversal) {
1757-
this.from((Traversal<?, Object>)fromVertex);
1758-
return this;
1759-
}
1760-
final Step<?,?> prev = this.asAdmin().getEndStep();
1761-
if (!(prev instanceof FromToModulating))
1762-
throw new IllegalArgumentException(String.format(
1763-
"The from() step cannot follow %s", prev.getClass().getSimpleName()));
1764-
1765-
this.asAdmin().getBytecode().addStep(Symbols.from, fromVertex);
1766-
((FromToModulating) prev).addFrom(fromVertex instanceof GValue ?
1767-
new GValueConstantTraversal<>((GValue<Object>) fromVertex) :
1768-
__.constant(fromVertex).asAdmin());
1769-
return this;
1770-
}
1771-
17721738
/**
17731739
* Map the {@link Traverser} to a {@link Double} according to the mathematical expression provided in the argument.
17741740
*

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AbstractAddElementStepPlaceholder.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
2222
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
23-
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
2423
import org.apache.tinkerpop.gremlin.process.traversal.lambda.GValueConstantTraversal;
2524
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
2625
import org.apache.tinkerpop.gremlin.process.traversal.step.GValueHolder;

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParentTest.java

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,6 @@ public static Iterable<Object[]> data() {
207207
List.of(__.constant("name"), __.constant("cole")),
208208
null, null
209209
},
210-
{AddEdgeStepContract.class,
211-
g.addE("label").from(1).to(2).property("name", __.constant("cole")),
212-
List.of(),
213-
List.of(__.constant("cole"), __.constant(1), __.constant(2)),
214-
null, null
215-
},
216210
{AddEdgeStepContract.class,
217211
g.addE("label").from(__.V(1)).to(__.V(2))
218212
.property("name", __.constant("cole"))
@@ -234,15 +228,9 @@ public static Iterable<Object[]> data() {
234228
null, null
235229
},
236230
{AddEdgeStepContract.class,
237-
g.addE("label").from(1).to(2).property(__.constant("name"), __.constant("cole")),
238-
List.of(),
239-
List.of(__.constant("name"), __.constant("cole"), __.constant(1), __.constant(2)),
240-
null, null
241-
},
242-
{AddEdgeStepContract.class,
243-
g.inject(1).addE("label").from(1).to(2).property("name", __.constant("cole")),
231+
g.addE("label").from(__.V(1)).to(__.V(2)).property(__.constant("name"), __.constant("cole")),
244232
List.of(),
245-
List.of(__.constant("cole"), __.constant(1), __.constant(2)),
233+
List.of(__.constant("name"), __.constant("cole"), __.V(1), __.V(2)),
246234
null, null
247235
},
248236
{AddEdgeStepContract.class,
@@ -258,9 +246,9 @@ public static Iterable<Object[]> data() {
258246
null, null
259247
},
260248
{AddEdgeStepContract.class,
261-
g.inject(1).addE("label").from(1).to(2).property(__.constant("name"), __.constant("cole")),
249+
g.inject(1).addE("label").from(__.V(1)).to(__.V(2)).property(__.constant("name"), __.constant("cole")),
262250
List.of(),
263-
List.of(__.constant("name"), __.constant("cole"), __.constant(1), __.constant(2)),
251+
List.of(__.constant("name"), __.constant("cole"), __.V(1), __.V(2)),
264252
null, null
265253
},
266254
{CallStepContract.class,

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStepTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal;
3030
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
3131
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
32+
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
3233
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
3334
import org.apache.tinkerpop.gremlin.process.traversal.step.GValueStepTest;
3435
import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -55,7 +56,7 @@ protected List<Traversal> getTraversals() {
5556
g.addE("created").property("a", "b"),
5657
g.addE("knows").property("a", "b").property("c", "e"),
5758
g.addE("knows").property("c", "e"),
58-
g.addE("knows").from(1).to(2).property("a", "b"),
59+
g.addE("knows").from(__.V(1)).to(__.V(2)).property("a", "b"),
5960
g.addE(GValue.of("label", "knows")).property("a", "b"),
6061
g.addE(GValue.of("label", "created")).property("a", GValue.of("prop", "b")),
6162
g.addE(GValue.of("label", "knows")).property("a", GValue.of("prop1", "b")).property("c", GValue.of("prop2", "e")),
@@ -211,10 +212,10 @@ public void getGValuesShouldReturnAllGValues() {
211212
}
212213

213214
@Test
214-
public void getGValuesNonShouldReturnEmptyCollection() {
215+
public void getGValuesNoneShouldReturnEmptyCollection() {
215216
GraphTraversal.Admin<Edge, Edge> traversal = g.addE("likes")
216-
.from(1)
217-
.to(2)
217+
.from(__.V(1))
218+
.to(__.V(2))
218219
.property(T.id, "1234")
219220
.property("rating", "great")
220221
.asAdmin();

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected List<Traversal> getTraversals() {
6060
__.addE("created").property("a", "b"),
6161
__.addE("knows").property("a", "b").property("c", "e"),
6262
__.addE("knows").property("c", "e"),
63-
__.addE("knows").from(1).to(2).property("a", "b"),
63+
__.addE("knows").from(__.V(1)).to(__.V(2)).property("a", "b"),
6464
__.addE(GValue.of("label", "knows")).property("a", "b"),
6565
__.addE(GValue.of("label", "created")).property("a", GValue.of("prop", "b")),
6666
__.addE(GValue.of("label", "knows")).property("a", GValue.of("prop1", "b")).property("c", GValue.of("prop2", "e")),
@@ -304,10 +304,10 @@ public void getGValuesShouldReturnAllGValues() {
304304
}
305305

306306
@Test
307-
public void getGValuesNonShouldReturnEmptyCollection() {
307+
public void getGValuesNoneShouldReturnEmptyCollection() {
308308
GraphTraversal.Admin<Object, Edge> traversal = __.addE("likes")
309-
.from(1)
310-
.to(2)
309+
.from(__.V(1))
310+
.to(__.V(2))
311311
.property(T.id, "1234")
312312
.property("rating", "great")
313313
.asAdmin();

0 commit comments

Comments
 (0)