You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/05/07 14:54:30 UTC
[tinkerpop] 14/14: TINKERPOP-1682 Support by(T) on Property
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-1682
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 12a6ef0b9b071518fa4419cbbdbd7a34849fd8de
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Thu May 7 08:53:23 2020 -0400
TINKERPOP-1682 Support by(T) on Property
---
CHANGELOG.asciidoc | 1 +
docs/src/upgrade/release-3.5.x.asciidoc | 29 ++++++++
.../process/traversal/lambda/TokenTraversal.java | 19 +++++-
.../process/traversal/step/sideEffect/IoStep.java | 2 +-
.../ByModulatorOptimizationStrategy.java | 8 +--
.../structure/io/graphml/GraphMLWriter.java | 1 -
.../traversal/lambda/TokenTraversalTest.java | 78 ++++++++++++++++++++++
.../ByModulatorOptimizationStrategyTest.java | 12 ++--
gremlin-test/features/map/Select.feature | 24 +++++++
.../process/traversal/step/map/SelectTest.java | 32 ++++++++-
10 files changed, 191 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index a80aac5..9f4bf28 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
* Ensured better consistency of the use of `null` as arguments to mutation steps.
* Allowed `property(T.label,Object)` to be used if no value was supplied to `addV(String)`.
* Added a `Graph.Feature` for `supportsNullPropertyValues`.
+* Modified `TokenTraversal` to support `Property` thus `by(key)` and `by(value)` can now apply to `Edge` and meta-properties.
* Added `Grouping` step interface.
* Added `TraversalParent.replaceTraversal()` which can replace a direct child traversal.
* Added `ByModulatorOptimizationStrategy` which replaces certain standard traversals w/ optimized traversals (e.g. `TokenTraversal`).
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 50180dd..f1b2ee3 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -209,6 +209,35 @@ be replaced by `by(id)`, thus replacing a step-based traversal with a token-base
See: link:https://issues.apache.org/jira/browse/TINKERPOP-1682[TINKERPOP-1682]
+==== by(T) for Property
+
+The `Property` interface is not included in the hierarchy of `Element`. This means that an edge property or a
+meta-property are not considered elements the way that a `VertexProperty` is. As a result, some usages of `T` in
+relation to properties do not work consistently. One such example is `by(T)`, a token-based traversal, where the
+following works for a `VertexProperty` but will not for edge properties or meta-properties:
+
+[source,text]
+----
+gremlin> g.V(1).properties().as('a').select('a').by(key)
+==>name
+==>age
+----
+
+For a `Property` you would need to use `key()`-step:
+
+[source,text]
+----
+gremlin> g.E(11).properties().as('a').select(last,'a').by(key())
+==>weight
+----
+
+Aside from the inconsistency, this issue also presents a situation where performance is impacted as token-based
+traversals are inherently faster than step-based ones. In 3.5.0, this issue has been resolved in conjunction with the
+introduction of `ByModulatorOptimizationStrategy` which will optimize `by(key())` and `by(value())` to their
+appropriate token versions automatically.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1682[TINKERPOP-1682]
+
==== Python 2.x Support
The gremlinpython module no longer supports Python 2.x. Users must use Python 3 going forward. For the most part, from
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
index d41c402..e272ccf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.lambda;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.T;
import java.util.Objects;
@@ -27,7 +28,7 @@ import java.util.Objects;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class TokenTraversal<S extends Element, E> extends AbstractLambdaTraversal<S, E> {
+public final class TokenTraversal<S, E> extends AbstractLambdaTraversal<S, E> {
private E e;
private final T t;
@@ -43,7 +44,21 @@ public final class TokenTraversal<S extends Element, E> extends AbstractLambdaTr
@Override
public void addStart(final Traverser.Admin<S> start) {
- this.e = (E) this.t.apply(start.get());
+ final S s = start.get();
+ if (s instanceof Element)
+ this.e = (E) this.t.apply((Element) start.get());
+ else if (s instanceof Property) {
+ // T.apply() doesn't work on Property because the inheritance hierarchy doesn't make it an Element. have
+ // to special case it here. only T.key/value make any sense for it.
+ if (t == T.key)
+ this.e = (E) ((Property) s).key();
+ else if (t == T.value)
+ this.e = ((Property<E>) s).value();
+ else
+ throw new IllegalStateException(String.format("TokenTraversal support of Property does not allow selection by %s", t));
+ } else
+ throw new IllegalStateException(String.format("TokenTraversal support of %s does not allow selection by %s", s.getClass().getName(), t));
+
}
@Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/IoStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/IoStep.java
index 861b318..0ca02ee 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/IoStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/IoStep.java
@@ -188,7 +188,7 @@ public class IoStep<S> extends AbstractStep<S,S> implements ReadWriting {
final GryoMapper.Builder builder = GryoMapper.build();
detectRegistries().forEach(builder::addRegistry);
return GryoWriter.build().mapper(builder.create()).create();
- }else if (objectOrClass.equals(IO.graphml))
+ } else if (objectOrClass.equals(IO.graphml))
return GraphMLWriter.build().create();
else {
try {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategy.java
index 579dcf4..0faaef9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategy.java
@@ -32,6 +32,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.IdStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.LabelStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyKeyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyValueStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupSideEffectStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
@@ -95,12 +97,10 @@ public final class ByModulatorOptimizationStrategy extends AbstractTraversalStra
step.replaceLocalChild(traversal, new TokenTraversal<>(T.id));
} else if (singleStep instanceof LabelStep) {
step.replaceLocalChild(traversal, new TokenTraversal<>(T.label));
-/* todo: this fails for `Property`s (e.g. outE().property().as("a").select("a").by(key/value))
} else if (singleStep instanceof PropertyKeyStep) {
- step.setModulateByTraversal(n, new TokenTraversal<>(T.key));
+ step.replaceLocalChild(traversal, new TokenTraversal<>(T.key));
} else if (singleStep instanceof PropertyValueStep) {
- step.setModulateByTraversal(n, new TokenTraversal<>(T.value));
-*/
+ step.replaceLocalChild(traversal, new TokenTraversal<>(T.value));
} else if (singleStep instanceof IdentityStep) {
step.replaceLocalChild(traversal, new IdentityTraversal<>());
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
index 3991e4e..5c70e99 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversalTest.java
new file mode 100644
index 0000000..7d1ad54
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversalTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.lambda;
+
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_Traverser;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TokenTraversalTest {
+ @Test
+ public void shouldWorkOnVertex() {
+ final TokenTraversal<Vertex, Integer> t = new TokenTraversal<>(T.id);
+ final Vertex v = mock(Vertex.class);
+ when(v.id()).thenReturn(100);
+ t.addStart(new B_O_Traverser<>(v, 1).asAdmin());
+ assertEquals(100, t.next().intValue());
+ }
+
+ @Test
+ public void shouldWorkOnVertexProperty() {
+ final TokenTraversal<VertexProperty, Integer> t = new TokenTraversal<>(T.id);
+ final VertexProperty vo = mock(VertexProperty.class);
+ when(vo.id()).thenReturn(100);
+ t.addStart(new B_O_Traverser<>(vo, 1).asAdmin());
+ assertEquals(100, t.next().intValue());
+ }
+
+ @Test
+ public void shouldWorkOnEdge() {
+ final TokenTraversal<Edge, Integer> t = new TokenTraversal<>(T.id);
+ final Edge e = mock(Edge.class);
+ when(e.id()).thenReturn(100);
+ t.addStart(new B_O_Traverser<>(e, 1).asAdmin());
+ assertEquals(100, t.next().intValue());
+ }
+
+ @Test
+ public void shouldWorkOnPropertyKey() {
+ final TokenTraversal<Property<String>, String> t = new TokenTraversal<>(T.key);
+ final Property<String> p = mock(Property.class);
+ when(p.key()).thenReturn("name");
+ t.addStart(new B_O_Traverser<>(p, 1).asAdmin());
+ assertEquals("name", t.next());
+ }
+
+ @Test
+ public void shouldWorkOnPropertyValue() {
+ final TokenTraversal<Property<String>, String> t = new TokenTraversal<>(T.value);
+ final Property<String> p = mock(Property.class);
+ when(p.value()).thenReturn("marko");
+ t.addStart(new B_O_Traverser<>(p, 1).asAdmin());
+ assertEquals("marko", t.next());
+ }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
index 23c4576..a3a0044 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
@@ -90,14 +90,14 @@ public class ByModulatorOptimizationStrategyTest {
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.label()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.label),
});
- /*result.add(new Traversal[]{
+ originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.key()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.key),
});
- result.add(new Traversal[]{
+ originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.value()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.value),
- });*/
+ });
originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.identity()),
((GraphTraversal) traversal.asAdmin().clone()).by(),
@@ -137,14 +137,14 @@ public class ByModulatorOptimizationStrategyTest {
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.label().fold()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.label),
});
- /*result.add(new Traversal[]{
+ originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.key().fold()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.key),
});
- result.add(new Traversal[]{
+ originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.value().fold()),
((GraphTraversal) traversal.asAdmin().clone()).by(T.value),
- });*/
+ });
originalAndOptimized.add(new Traversal[]{
((GraphTraversal<?, ?>) traversal.asAdmin().clone()).by(__.identity()),
((GraphTraversal) traversal.asAdmin().clone()).by(),
diff --git a/gremlin-test/features/map/Select.feature b/gremlin-test/features/map/Select.feature
index 0d4913a..8c759d6 100644
--- a/gremlin-test/features/map/Select.feature
+++ b/gremlin-test/features/map/Select.feature
@@ -744,3 +744,27 @@ Feature: Step - select()
| s[a,b] |
| s[c] |
And the graph should return 6 for count of "g.V().as(\"a\", \"b\").out().as(\"c\").path().select(Column.keys)"
+
+ Scenario: g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX
+ Given the modern graph
+ And using the parameter e11Id defined as "e[josh-created->lop].id"
+ And the traversal of
+ """
+ g.E(e11Id).properties("weight").as("a").select("a").by(T.key)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | weight |
+
+ Scenario: g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX
+ Given the modern graph
+ And using the parameter e11Id defined as "e[josh-created->lop].id"
+ And the traversal of
+ """
+ g.E(e11Id).properties("weight").as("a").select("a").by(T.value)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[0.4].d |
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
index 0f5afbd..4e5d4a0 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +39,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Stream;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.CREW;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
@@ -155,6 +155,10 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Vertex> get_g_VX1X_asXaX_repeatXout_asXaXX_timesX2X_selectXlast_aX(final Object v1Id);
+ public abstract Traversal<Edge, Object> get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX(final Object e11Id);
+
+ public abstract Traversal<Edge, Object> get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX(final Object e11Id);
+
// when labels don't exist
public abstract Traversal<Vertex, String> get_g_V_untilXout_outX_repeatXin_asXaXX_selectXaX_byXtailXlocalX_nameX();
@@ -703,6 +707,22 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
assertEquals(Collections.emptyList(), traversal.toList());
}
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX() {
+ final Traversal<Edge, Object> traversal = get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX(convertToEdgeId("josh", "created", "lop"));
+ printTraversalForm(traversal);
+ assertEquals("weight", traversal.next());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX() {
+ final Traversal<Edge, Object> traversal = get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX(convertToEdgeId("josh", "created", "lop"));
+ printTraversalForm(traversal);
+ assertEquals(0.4d, (double) traversal.next(), 0.0001d);
+ }
+
// when labels don't exist
@Test
@@ -1074,6 +1094,16 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
return g.V().as("a").where(__.out("knows")).<Vertex>select("a");
}
+ @Override
+ public Traversal<Edge, Object> get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX(final Object e11Id) {
+ return g.E(e11Id).properties("weight").as("a").select("a").by(T.key);
+ }
+
+ @Override
+ public Traversal<Edge, Object> get_g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX(final Object e11Id) {
+ return g.E(e11Id).properties("weight").as("a").select("a").by(T.value);
+ }
+
// select columns test
@Override