Repository: tinkerpop Updated Branches: refs/heads/TINKERPOP-1799 74c617b08 -> d5ed2f7cb (forced update)
first non-tested implementation of math()-step. It uses the @twilmes model which parses a String representation of the expression. Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/6f1a1f70 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/6f1a1f70 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/6f1a1f70 Branch: refs/heads/TINKERPOP-1799 Commit: 6f1a1f7028f285c6e45b7e7596320710b91114c5 Parents: 4728916 Author: Marko A. Rodriguez <[email protected]> Authored: Tue Oct 3 17:37:10 2017 -0600 Committer: Marko A. Rodriguez <[email protected]> Committed: Tue Oct 3 17:37:10 2017 -0600 ---------------------------------------------------------------------- gremlin-core/pom.xml | 5 + .../traversal/dsl/graph/GraphTraversal.java | 14 ++ .../gremlin/process/traversal/dsl/graph/__.java | 7 + .../process/traversal/step/map/MathStep.java | 128 +++++++++++++++++++ .../process/traversal/util/TraversalHelper.java | 2 +- .../traversal/dsl/graph/GraphTraversalTest.java | 2 + .../Process/Traversal/GraphTraversal.cs | 9 ++ .../src/Gremlin.Net/Process/Traversal/__.cs | 8 ++ .../gremlin_python/process/graph_traversal.py | 12 ++ .../structure/TinkerGraphPlayTest.java | 42 +----- 10 files changed, 188 insertions(+), 41 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/pom.xml ---------------------------------------------------------------------- diff --git a/gremlin-core/pom.xml b/gremlin-core/pom.xml index 3d9e701..30c1b9f 100644 --- a/gremlin-core/pom.xml +++ b/gremlin-core/pom.xml @@ -68,6 +68,11 @@ limitations under the License. <artifactId>javapoet</artifactId> <version>1.8.0</version> </dependency> + <dependency> + <groupId>net.objecthunter</groupId> + <artifactId>exp4j</artifactId> + <version>0.4.8</version> + </dependency> <!-- LOGGING --> <dependency> <groupId>org.slf4j</groupId> http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java ---------------------------------------------------------------------- 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 44a7a3a..8cedbfb 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 @@ -86,6 +86,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaFlatMapStep import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.MathStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep; @@ -1097,6 +1098,18 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { return this; } + /** + * Map the {@link Traverser} to a {@link Double} according to the mathematical expression provided in the argument. + * + * @param expression the mathematical expression with variables refering to scope variables. + * @return the traversal with the {@link MathStep} added. + * @since 3.3.1 + */ + public default GraphTraversal<S, Double> math(final String expression) { + this.asAdmin().getBytecode().addStep(Symbols.math, expression); + return this.asAdmin().addStep(new MathStep<>(this.asAdmin(), expression)); + } + ///////////////////// FILTER STEPS ///////////////////// /** @@ -2691,6 +2704,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { public static final String value = "value"; public static final String path = "path"; public static final String match = "match"; + public static final String math = "math"; public static final String sack = "sack"; public static final String loops = "loops"; public static final String project = "project"; http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java ---------------------------------------------------------------------- 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 9dc3a93..39e5258 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 @@ -498,6 +498,13 @@ public class __ { return __.<A>start().addE(edgeLabelTraversal); } + /** + * @see GraphTraversal#math(String) + */ + public static <A> GraphTraversal<A, Double> math(final String expression) { + return __.<A>start().math(expression); + } + ///////////////////// FILTER STEPS ///////////////////// /** http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java new file mode 100644 index 0000000..645ea06 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tinkerpop.gremlin.process.traversal.step.map; + +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.apache.tinkerpop.gremlin.process.traversal.Pop; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating; +import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class MathStep<S> extends MapStep<S, Double> implements ByModulating, TraversalParent, Scoping { + + private static final String CURRENT = "_"; + private final String equation; + private final Set<String> variables; + private final Expression expression; + private TraversalRing<Object, Number> traversalRing = new TraversalRing<>(); + + public MathStep(final Traversal.Admin traversal, final String equation) { + super(traversal); + this.equation = equation; + final Set<String> labels = TraversalHelper.getLabels(TraversalHelper.getRootTraversal(this.getTraversal())); + this.variables = new LinkedHashSet<>(); + for (final String label : labels) { + if (this.equation.contains(label)) + this.variables.add(label); + } + if (this.equation.contains(CURRENT)) + this.variables.add(CURRENT); + this.expression = new ExpressionBuilder(this.equation) + .variables(this.variables) + .build(); + } + + @Override + protected Double map(final Traverser.Admin<S> traverser) { + for (final String var : this.variables) { + this.expression.setVariable(var, TraversalUtil.applyNullable( + var.equals(CURRENT) ? + traverser.get() : + this.getNullableScopeValue(Pop.last, var, traverser), + this.traversalRing.next()).doubleValue()); + } + this.traversalRing.reset(); + return this.expression.evaluate(); + } + + @Override + public void modulateBy(final Traversal.Admin<?, ?> selectTraversal) { + this.traversalRing.addTraversal(this.integrateChild(selectTraversal)); + } + + @Override + public String toString() { + return StringFactory.stepString(this, this.equation, this.traversalRing); + } + + @Override + public int hashCode() { + return super.hashCode() ^ this.equation.hashCode() ^ this.traversalRing.hashCode(); + } + + @Override + public List<Traversal.Admin<Object, Number>> getLocalChildren() { + return this.traversalRing.getTraversals(); + } + + @Override + public void reset() { + super.reset(); + this.traversalRing.reset(); + } + + @Override + public MathStep<S> clone() { + final MathStep<S> clone = (MathStep<S>) super.clone(); + clone.traversalRing = this.traversalRing.clone(); + return clone; + } + + @Override + public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) { + super.setTraversal(parentTraversal); + this.traversalRing.getTraversals().forEach(this::integrateChild); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT, TraverserRequirement.SIDE_EFFECTS); + } + + @Override + public Set<String> getScopeKeys() { + return this.variables; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java index 56f2eca..9db3ea8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java @@ -560,7 +560,7 @@ public final class TraversalHelper { } public static Set<String> getLabels(final Traversal.Admin<?, ?> traversal) { - return TraversalHelper.getLabels(new HashSet<>(), traversal); + return TraversalHelper.getLabels(new LinkedHashSet<>(), traversal); } private static Set<String> getLabels(final Set<String> labels, final Traversal.Admin<?, ?> traversal) { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java index cfa63fa..d3fe1c4 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java @@ -112,6 +112,8 @@ public class GraphTraversalTest { list.add(arguments[0] = (long) (Math.abs(random.nextInt(10)))); list.add(arguments[1] = (long) (Math.abs(random.nextInt(10) + 100))); } + } else if (stepMethod.getName().equals("math")) { + list.add(arguments[0] = random.nextInt(100) + " + " + random.nextInt(100)); } else { for (int i = 0; i < stepMethod.getParameterTypes().length; i++) { final Class<?> type = stepMethod.getParameterTypes()[i]; http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index 00bfd4b..6c6d174 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -495,6 +495,15 @@ namespace Gremlin.Net.Process.Traversal } /// <summary> + /// Adds the math step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal< S , Double > Math (params object[] args) + { + Bytecode.AddStep("math", args); + return Wrap< S , Double >(this); + } + + /// <summary> /// Adds the max step to this <see cref="GraphTraversal{SType, EType}" />. /// </summary> public GraphTraversal< S , E2 > Max<E2> (params object[] args) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs index dfe114d..0e0c527 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs @@ -409,6 +409,14 @@ namespace Gremlin.Net.Process.Traversal } /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the math step to that traversal. + /// </summary> + public static GraphTraversal<object, Double> Math(params object[] args) + { + return new GraphTraversal<object, object>().Math(args); + } + + /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the max step to that traversal. /// </summary> public static GraphTraversal<object, E2> Max<E2>(params object[] args) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py index f7bb201..d5630c0 100644 --- a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py +++ b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py @@ -317,6 +317,10 @@ class GraphTraversal(Traversal): self.bytecode.add_step("match", *args) return self + def math(self, *args): + self.bytecode.add_step("math", *args) + return self + def max(self, *args): self.bytecode.add_step("max", *args) return self @@ -697,6 +701,10 @@ class __(object): return cls.graph_traversal(None, None, Bytecode()).match(*args) @classmethod + def math(cls, *args): + return cls.graph_traversal(None, None, Bytecode()).math(*args) + + @classmethod def max(cls, *args): return cls.graph_traversal(None, None, Bytecode()).max(*args) @@ -1046,6 +1054,10 @@ def match(*args): return __.match(*args) statics.add_static('match', match) +def math(*args): + return __.math(*args) +statics.add_static('math', math) + def max(*args): return __.max(*args) statics.add_static('max', max) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6f1a1f70/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java ---------------------------------------------------------------------- diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java index eeb4f38..0af81bd 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java @@ -50,6 +50,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.P.lt; import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.choose; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.has; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in; @@ -70,48 +71,9 @@ public class TinkerGraphPlayTest { public void testPlay8() throws Exception { Graph graph = TinkerFactory.createModern(); GraphTraversalSource g = graph.traversal(); - - final Traversal<?, ?> traversal = g.V().repeat(out()).times(2).groupCount().by("name").select(Column.keys).order().by(Order.decr); - final Bytecode bytecode = traversal.asAdmin().getBytecode(); - //final JavaTranslator translator = JavaTranslator.of(g); - final Map<Bytecode, Traversal.Admin<?, ?>> cache = new HashMap<>(); - cache.put(bytecode, traversal.asAdmin()); - final HashSet<?> result = new LinkedHashSet<>(Arrays.asList("ripple", "lop")); - - System.out.println("BYTECODE: " + bytecode + "\n"); - System.out.println("Bytecode->Traversal.clone() cache: " + TimeUtil.clock(1000, () -> { - final Traversal.Admin<?, ?> t = cache.get(bytecode).clone(); - //assertEquals(result, t.next()); - })); - - System.out.println("Bytecode->JavaTranslator call : " + TimeUtil.clock(1000, () -> { - final Traversal t = JavaTranslator.of(g).translate(bytecode); - //assertEquals(result, t.next()); - })); - - System.out.println("\n==Second test with reversed execution==\n"); - - System.out.println("BYTECODE: " + bytecode + "\n"); - System.out.println("Bytecode->JavaTranslator call : " + TimeUtil.clock(1000, () -> { - final Traversal t = JavaTranslator.of(g).translate(bytecode); - //assertEquals(result, t.next()); - })); - - System.out.println("Bytecode->Traversal.clone() cache: " + TimeUtil.clock(1000, () -> { - final Traversal.Admin<?, ?> t = cache.get(bytecode).clone(); - //assertEquals(result, t.next()); - })); + System.out.println(g.V().as("a").out("knows").math("((a ^ _) / a)").by("age").by(bothE().count()).toList()); } - /* @Test - public void testTraversalDSL() throws Exception { - Graph g = TinkerFactory.createClassic(); - assertEquals(2, g.of(TinkerFactory.SocialTraversal.class).people("marko").knows().name().toList().size()); - g.of(TinkerFactory.SocialTraversal.class).people("marko").knows().name().forEachRemaining(name -> assertTrue(name.equals("josh") || name.equals("vadas"))); - assertEquals(1, g.of(TinkerFactory.SocialTraversal.class).people("marko").created().name().toList().size()); - g.of(TinkerFactory.SocialTraversal.class).people("marko").created().name().forEachRemaining(name -> assertEquals("lop", name)); - }*/ - @Test @Ignore public void benchmarkStandardTraversals() throws Exception {
