diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 2c39a6bf78d..f3f4bc2fcc3 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima [[release-4-0-0-beta-1]] === TinkerPop 4.0.0-beta.1 (January 17, 2025) +* Added the `PopContaining` interface designed to get label and `Pop` combinations held in a `PopInstruction` object. * Added support for deserialization of `Set` for `gremlin-javascript`. * Added grammar-based `Translator` for all languages including explicit ones for Java and anonymization. * Removed old `Translator` infrastructure. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java new file mode 100644 index 00000000000..89fdf28e582 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java @@ -0,0 +1,103 @@ +/* + * 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; + +import java.util.Objects; +import java.util.Set; + +import org.apache.tinkerpop.gremlin.process.traversal.Pop; + +/** + * The {@code PopContaining} interface is implemented by traversal steps that maintain Pop instructions + * for label access. It provides a mechanism to track and manage how labeled elements should + * be accessed using {@link Pop} semantics (first, last, all, or mixed). + * + * In Gremlin traversals, various elements can be labeled and later accessed via these labels. + * The {@link Pop} enum determines how to access these labeled elements when there are multiple + * values associated with the same label. + * + *
+ * {@code
+ * // Simple example with default Pop.last behavior
+ * gremlin> g.V().as("a").out().as("a").select("a")
+ * ==>[v[2]]  // returns the last element labeled "a"
+ *
+ * // Using Pop.first to get the first labeled element
+ * gremlin> g.V().as("a").out().as("a").select(first, "a")
+ * ==>[v[1]]  // returns the first element labeled "a"
+ *
+ * // Using Pop.all to get all labeled elements
+ * gremlin> g.V().as("a").out().as("a").select(all, "a")
+ * ==>[v[1], v[2]]  // returns all elements labeled "a"
+ * }
+ * 
+ * + * Steps implementing this interface maintain a collection of {@link PopInstruction} objects, each containing + * a label and a {@link Pop} value that specifies how to access elements with that label. + * + */ +public interface PopContaining { + public Set getPopInstructions(); + /** + * A class for storing the Scope Context. It has two elements: + * - label: String + * - pop: Pop value + */ + class PopInstruction { + private final Pop pop; + private final String label; + + public PopInstruction(String label, Pop pop) { + this.pop = pop; + this.label = label; + } + + public PopInstruction(final Pop pop, final String label) { + this.pop = pop; + this.label = label; + } + + public PopInstruction() { + this.pop = null; + this.label = ""; + } + + public String getLabel() { + return label; + } + + public Pop getPop() { + return pop; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + final PopInstruction that = (PopInstruction) o; + return getPop() == that.getPop() && Objects.equals(getLabel(), that.getLabel()); + } + + @Override + public int hashCode() { + return Objects.hash(getPop(), getLabel()); + } + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java index e2c0e17747e..a765e778757 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java @@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -105,7 +106,7 @@ * @author Marko A. Rodriguez (http://markorodriguez.com) * @author Stephen Mallette (http://stephen.genoprime.com) */ -public interface Scoping { +public interface Scoping extends PopContaining { public enum Variable {START, END} @@ -166,6 +167,23 @@ public default S getNullableScopeValue(final Pop pop, final String key, fina */ public Set getScopeKeys(); + /** + * Used to get PopInstruction of a Step that implements Scoping. PopInstruction includes the labels it needs, and the + * pop type for each label. + * + * @return A Set of {@link PopInstruction} values which contain the label and Pop value + */ + @Override + public default HashSet getPopInstructions() { + final Set labels = this.getScopeKeys(); + final HashSet scopingInfoSet = new HashSet<>(); + for (final String label : labels) { + final PopInstruction scopingInfo = new PopInstruction(Pop.last, label); + scopingInfoSet.add(scopingInfo); + } + return scopingInfoSet; + } + public static class KeyNotFoundException extends Exception { private final Object key; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java index 695d2972fc6..3a43f7b74ce 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java @@ -21,16 +21,18 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import java.util.Collections; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public interface TraversalParent extends AutoCloseable { +public interface TraversalParent extends PopContaining, AutoCloseable { public default List> getGlobalChildren() { return Collections.emptyList(); @@ -95,4 +97,16 @@ default void close() throws Exception { traversal.close(); } } + + @Override + public default HashSet getPopInstructions() { + final HashSet scopingInfos = new HashSet<>(); + for (final Traversal.Admin local: this.getLocalChildren()) { + scopingInfos.addAll(TraversalHelper.getPopInstructions(local)); + } + for (final Traversal.Admin global: this.getGlobalChildren()) { + scopingInfos.addAll(TraversalHelper.getPopInstructions(global)); + } + return scopingInfos; + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java index ad4a762fe01..4bfc97c52fa 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java @@ -189,6 +189,14 @@ public Set getScopeKeys() { return null == this.dedupLabels ? Collections.emptySet() : this.dedupLabels; } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public void processAllStarts() { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java index cffe3ebc8c3..69b054446af 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java @@ -132,6 +132,14 @@ public Set getScopeKeys() { return Collections.unmodifiableSet(this.scopeKeys); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public WherePredicateStep clone() { final WherePredicateStep clone = (WherePredicateStep) super.clone(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java index dde900348a0..1fa30b98b95 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java @@ -147,6 +147,14 @@ public Set getKeepLabels() { return this.keepLabels; } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + ////////////////////////////// public static class WhereStartStep extends ScalarMapStep implements Scoping { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java index 81f804f3e26..1c7d5a90512 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java @@ -43,6 +43,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -89,6 +90,13 @@ public Set getScopeKeys() { return this.parameters.getReferencedLabels(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public void configure(final Object... keyValues) { this.parameters.set(this, keyValues); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java index 258f8421ab2..d9097f2ffef 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java @@ -40,6 +40,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -86,6 +87,13 @@ public Set getScopeKeys() { return this.parameters.getReferencedLabels(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public void configure(final Object... keyValues) { this.parameters.set(this, keyValues); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java index d8dad9dbda0..c7a27e2eb82 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java @@ -38,6 +38,7 @@ import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -85,6 +86,13 @@ public Set getScopeKeys() { return this.parameters.getReferencedLabels(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public List> getLocalChildren() { return this.parameters.getTraversals(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java index cfde7d84fce..bcf21c7bc72 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java @@ -34,6 +34,7 @@ import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -78,6 +79,13 @@ public Set getScopeKeys() { return this.parameters.getReferencedLabels(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public List> getLocalChildren() { return this.parameters.getTraversals(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java index c4b50958d90..39d7d005867 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java @@ -34,6 +34,7 @@ import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; @@ -168,6 +169,14 @@ public Set getScopeKeys() { return variables; } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public void setKeepLabels(final Set labels) { this.keepLabels = labels; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java index 6de3970236f..055f3029966 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java @@ -218,6 +218,14 @@ public Set getScopeKeys() { return this.scopeKeys; } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public String toString() { return StringFactory.stepString(this, this.dedupLabels, this.connective, this.matchTraversals); 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 index e36a1311c8a..25330705429 100644 --- 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 @@ -165,6 +165,14 @@ public Set getScopeKeys() { return this.expression.getVariables(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public void setKeepLabels(final Set labels) { this.keepLabels = labels; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java index b2de4b002c9..dd6661eb77d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java @@ -134,6 +134,18 @@ public Set getScopeKeys() { return Collections.singleton(this.selectKey); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + final Set labels = this.getScopeKeys(); + for (String label : labels) { + final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label); + popInstructions.add(scopingInfo); + } + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + public Pop getPop() { return this.pop; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java index 24c9771ed2c..d47c9037396 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java @@ -34,7 +34,6 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -149,6 +148,18 @@ public Set getScopeKeys() { return this.selectKeysSet; } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + final Set labels = this.getScopeKeys(); + for (final String label : labels) { + final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label); + popInstructions.add(scopingInfo); + } + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + /** * Get the keys for this SelectStep. Unlike {@link SelectStep#getScopeKeys()}, this returns a list possibly with * a duplicate key. This guarantees to return the keys in the same order as passed in. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java index ff9f0996f5d..0eea87f36cc 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java @@ -85,6 +85,18 @@ public Set getScopeKeys() { return Collections.emptySet(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + final Set labels = this.getScopeKeys(); + for (String label : labels) { + final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label); + popInstructions.add(scopingInfo); + } + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public String toString() { return StringFactory.stepString(this, this.pop, this.keyTraversal, this.selectTraversal); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java index 3dfef89285a..5f869b6df31 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java @@ -38,6 +38,7 @@ import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedProperty; import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedVertexProperty; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -70,6 +71,14 @@ public Set getScopeKeys() { return this.parameters.getReferencedLabels(); } + @Override + public HashSet getPopInstructions() { + final HashSet popInstructions = new HashSet<>(); + popInstructions.addAll(Scoping.super.getPopInstructions()); + popInstructions.addAll(TraversalParent.super.getPopInstructions()); + return popInstructions; + } + @Override public List> getLocalChildren() { return this.parameters.getTraversals(); 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 19117d6e9ab..44df37cfd02 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 @@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.lambda.TokenTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating; import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; 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.step.branch.RepeatStep; @@ -54,7 +55,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; @@ -788,4 +788,22 @@ public static void applySingleLevelStrategies(final Traversal.Admin parent } else return (T) traversal.addStep(new HasStep<>(traversal, hasContainer)); } + + /** + * Used to get PopInstruction of a traversal. Pop Instruction includes the labels it needs, and the pop type for each label. + * + * @param traversal the traversal to get Scope Context for + * @param the traversal type + * @return A Set of {@link org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining.PopInstruction} values which contain the label and Pop value + */ + public static > Set getPopInstructions(final T traversal) { + final Set scopingInfos = new HashSet<>(); + for (final Step step: traversal.getSteps()) { + if (step instanceof PopContaining) { + scopingInfos.addAll(((PopContaining) step).getPopInstructions()); + } + } + return scopingInfos; + } + } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java new file mode 100644 index 00000000000..1f429659faa --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java @@ -0,0 +1,48 @@ +/* + * 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; + +import java.util.HashSet; + +import org.apache.tinkerpop.gremlin.process.traversal.Pop; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; + +/** + * This class is responsible for building test data for `PopInstruction` Unit tests. + * It provides methods to create a set of `PopInstruction` test data objects. + * + */ +public class TestDataBuilder { + + // Helper function to create a Set of `PopInstruction` values + public static HashSet createPopInstructionSet(final Object[]... pairs) { + final HashSet popInstructions = new HashSet<>(); + + // Each pair should contain a name (String) and a Pop value + for (final Object[] pair : pairs) { + if (pair.length == 2 && pair[0] instanceof String && pair[1] instanceof Pop) { + popInstructions.add(new PopContaining.PopInstruction((String)pair[0], (Pop)pair[1])); + } else { + throw new IllegalArgumentException("Invalid pair: " + pair); + } + } + + return popInstructions; + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java index d7beabe555d..a763e90ea79 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java @@ -18,13 +18,19 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.filter; +import org.apache.tinkerpop.gremlin.TestDataBuilder; +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.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; +import org.junit.Test; import java.util.Arrays; +import java.util.HashSet; import java.util.List; -import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * @author Daniel Kuppitz (http://gremlin.guru) @@ -43,4 +49,16 @@ protected List getTraversals() { public void shouldThrowForMultipleByModulators() { __.dedup().by("name").by("age"); } + + @Test + public void shouldObtainPopInstructions() { + final DedupGlobalStep dedupGlobalStep = new DedupGlobalStep(__.identity().asAdmin(), "label1", "label2", "label1"); + + final HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"label1", Pop.last}, + new Object[]{"label2", Pop.last} + ); + + assertEquals(dedupGlobalStep.getPopInstructions(), popInstructionSet); + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java index fa9adeb1f7a..24700083f5a 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java @@ -18,15 +18,20 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.filter; +import org.apache.tinkerpop.gremlin.TestDataBuilder; import org.apache.tinkerpop.gremlin.process.traversal.P; +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.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal; import org.junit.Test; - import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Optional; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as; import static org.junit.Assert.assertEquals; @@ -65,4 +70,30 @@ public void shouldRequirePathsAccordingly() { assertEquals(traversalPath[0], ((Traversal.Admin) traversalPath[1]).getTraverserRequirements().contains(TraverserRequirement.LABELED_PATH)); } } + + @Test + public void shouldObtainPopInstructions() { + + // Testing WherePredicate Step + final WherePredicateStep wherePredicateStep = new WherePredicateStep(__.identity().asAdmin(), Optional.of("key1"), P.neq("label1")); + + HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"key1", Pop.last}, + new Object[]{"label1", Pop.last} + ); + + assertEquals(wherePredicateStep.getPopInstructions(), popInstructionSet); + + // Testing WhereTraversal Test + final WhereTraversalStep whereTraversalStep = new WhereTraversalStep<>(new DefaultTraversal(), __.as("x").select(Pop.first, "a", "b").asAdmin()); + + popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.last}, + new Object[]{"a", Pop.first}, + new Object[]{"b", Pop.first} + ); + + assertEquals(whereTraversalStep.getPopInstructions(), popInstructionSet); + + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java index f4282aec696..d5742ba1f02 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java @@ -18,13 +18,23 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; +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.process.traversal.step.StepTest; +import org.junit.Test; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import static org.junit.Assert.assertEquals; + + +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; + + /** * @author Daniel Kuppitz (http://gremlin.guru) */ @@ -39,4 +49,24 @@ protected List getTraversals() { __.addE("knows").property("c", "d") ); } + + @Test + public void shouldObtainPopInstructions() { + // Edge Step Test + final AddEdgeStep addEdgeStep = new AddEdgeStep<>(__.identity().asAdmin(), + (Traversal.Admin) __.select(Pop.first, "b").select("a")); + + final HashSet expectedOutput = TestDataBuilder.createPopInstructionSet( + new Object[]{"b", Pop.first}, + new Object[]{"a", Pop.last} + ); + + assertEquals(addEdgeStep.getPopInstructions(), expectedOutput); + + // Edge Step Start test + final AddEdgeStartStep addEdgeStartStep = new AddEdgeStartStep(__.identity().asAdmin(), + __.select(Pop.first, "b").select("a")); + + assertEquals(addEdgeStartStep.getPopInstructions(), expectedOutput); + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepTest.java index d5ae0ec9009..bda33a2507b 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStepTest.java @@ -18,7 +18,13 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import java.util.HashSet; + +import org.apache.tinkerpop.gremlin.TestDataBuilder; +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.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.util.function.TraverserSetSupplier; @@ -49,4 +55,25 @@ public void shouldDefaultTheLabelIfNullTraversal() { final AddVertexStep step = new AddVertexStep(t, (String) null); assertEquals(Vertex.DEFAULT_LABEL, starStep.getParameters().getRaw().get(T.label).get(0)); } + + @Test + public void shouldObtainPopInstructions() { + // Vertex Step Test + final AddVertexStep addVertexStep = new AddVertexStep(__.identity().asAdmin(), + (Traversal.Admin) __.select(Pop.first, "b").select("a").select(Pop.last, "c")); + + final HashSet expectedOutput = TestDataBuilder.createPopInstructionSet( + new Object[]{"b", Pop.first}, + new Object[]{"a", Pop.last}, + new Object[]{"c", Pop.last} + ); + + assertEquals(addVertexStep.getPopInstructions(), expectedOutput); + + // Vertex Start Step Test + final AddVertexStartStep addVertexStartStep = new AddVertexStartStep(__.identity().asAdmin(), + __.select(Pop.first, "b").select("a").select(Pop.last, "c")); + + assertEquals(addVertexStartStep.getPopInstructions(), expectedOutput); + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStepTest.java index cf66458f451..977afa37f23 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStepTest.java @@ -18,8 +18,11 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; +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.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; @@ -29,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asMap; @@ -180,4 +184,17 @@ public void shouldPrioritizeVertexPropertiesOverScopeVariables() { __.__("Marko").as("name"). constant(vertex).format("Hello %{name}").next()); } + + @Test + public void shouldObtainPopInstructions() { + final FormatStep formatStep = new FormatStep(__.identity().asAdmin(), "%{Hello} %{world}"); + + final HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"Hello", Pop.last}, + new Object[]{"world", Pop.last} + ); + + assertEquals(formatStep.getPopInstructions(), popInstructionSet); + + } } \ No newline at end of file diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java index 9b204cf2857..f2dd400494b 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java @@ -18,10 +18,13 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; import org.apache.tinkerpop.gremlin.process.traversal.P; +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.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.ConnectiveStep; @@ -418,4 +421,20 @@ public void shouldCalculateStartLabelCorrectly() { assertEquals("a", MatchStep.Helper.computeStartLabel(((MatchStep) traversal.getStartStep()).getGlobalChildren())); } + @Test + public void shouldObtainPopInstructions() { + final Traversal.Admin traversal = __.match(as("a").out().as("b"), as("c").path().as("d")).asAdmin(); + final MatchStep matchStep = (MatchStep) traversal.getStartStep(); + + final HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last}, + new Object[]{"b", Pop.last}, + new Object[]{"c", Pop.last}, + new Object[]{"d", Pop.last} + ); + + + assertEquals(matchStep.getPopInstructions(), popInstructionSet); + } + } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStepTest.java index 0a9141c3355..6dd0a147cc2 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStepTest.java @@ -19,14 +19,19 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; +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.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; @@ -71,4 +76,20 @@ public void shouldParseVariablesWithFunctionNameMatches() { assertEquals(Arrays.asList("number1", "expected_value"), new ArrayList<>(MathStep.getVariables("number1-expected_value"))); } + @Test + public void shouldObtainPopInstructions() { + final GraphTraversal traversal = __.identity(); + + final MathStep mathStep = new MathStep<>((Traversal.Admin) traversal, "a + b - c"); + + // Expected Output + final HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last}, + new Object[]{"b", Pop.last}, + new Object[]{"c", Pop.last} + ); + + assertEquals(popInstructionSet, mathStep.getPopInstructions()); + } + } \ No newline at end of file diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStepTest.java index 480c0b1f09e..1ce46d47d2e 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStepTest.java @@ -18,14 +18,18 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; 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.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.junit.Test; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import static org.junit.Assert.assertEquals; @@ -61,4 +65,48 @@ public void shouldRequirePathsAccordingly() { assertEquals(traversalPath[0], ((Traversal.Admin) traversalPath[1]).getTraverserRequirements().contains(TraverserRequirement.LABELED_PATH)); } } + + @Test + public void shouldObtainPopInstructions() { + final GraphTraversal traversal = __.identity(); + + // Expected Output + HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.all} + ); + + // Pop.all + final SelectOneStep selectOneStepAll = new SelectOneStep((Traversal.Admin) traversal, Pop.all, "x"); + + assertEquals(selectOneStepAll.getPopInstructions(), popInstructionSet); + + + // Pop.last + final SelectOneStep selectOneStepLast = new SelectOneStep<>((Traversal.Admin) traversal, Pop.last, "x"); + popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.last} + ); + + assertEquals(selectOneStepLast.getPopInstructions(), popInstructionSet); + + + // Pop.first + final SelectOneStep selectOneStepFirst = new SelectOneStep<>((Traversal.Admin) traversal, Pop.first, "x"); + popInstructionSet = popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.first} + ); + + assertEquals(selectOneStepFirst.getPopInstructions(), popInstructionSet); + + + // Pop.mixed + final SelectOneStep selectOneStepMixed = new SelectOneStep<>((Traversal.Admin) traversal, Pop.mixed, "x"); + popInstructionSet = popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.mixed} + ); + + + assertEquals(selectOneStepMixed.getPopInstructions(), popInstructionSet); + + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStepTest.java index 5393403d811..4d39cdd239d 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStepTest.java @@ -18,14 +18,18 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; +import org.apache.tinkerpop.gremlin.TestDataBuilder; 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.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.junit.Test; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import static org.junit.Assert.assertEquals; @@ -63,4 +67,50 @@ public void shouldRequirePathsAccordingly() { assertEquals(traversalPath[0], ((Traversal.Admin) traversalPath[1]).getTraverserRequirements().contains(TraverserRequirement.LABELED_PATH)); } } + + @Test + public void shouldObtainPopInstructions() { + final GraphTraversal traversal = __.identity(); + + // 2 keys, and Pop.all + final SelectStep selectStepAll = new SelectStep<>((Traversal.Admin) traversal, Pop.all, "x", "y"); + HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.all}, + new Object[]{"y", Pop.all} + ); + + assertEquals(selectStepAll.getPopInstructions(), popInstructionSet); + + + // 3 keys, and Pop.last + final SelectStep selectStepLast = new SelectStep<>((Traversal.Admin) traversal, Pop.last, "x", "y", "z"); + popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.last}, + new Object[]{"y", Pop.last}, + new Object[]{"z", Pop.last} + ); + + assertEquals(selectStepLast.getPopInstructions(), popInstructionSet); + + + // 2 keys, and Pop.first + final SelectStep selectStepFirst = new SelectStep<>((Traversal.Admin) traversal, Pop.first, "x", "y"); + popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.first}, + new Object[]{"y", Pop.first} + ); + + assertEquals(selectStepFirst.getPopInstructions(), popInstructionSet); + + + // 2 keys, and Pop.mixed + final SelectStep selectStepMixed = new SelectStep<>((Traversal.Admin) traversal, Pop.mixed, "x", "y"); + popInstructionSet = TestDataBuilder.createPopInstructionSet( + new Object[]{"x", Pop.mixed}, + new Object[]{"y", Pop.mixed} + ); + + assertEquals(selectStepMixed.getPopInstructions(), popInstructionSet); + + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStepTest.java new file mode 100644 index 00000000000..1e0198ba573 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStepTest.java @@ -0,0 +1,56 @@ +/* + * 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 anmvn limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.process.traversal.step.map; + +import static org.junit.Assert.assertEquals; + +import java.util.HashSet; +import org.apache.tinkerpop.gremlin.TestDataBuilder; +import org.apache.tinkerpop.gremlin.process.traversal.Pop; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; +import org.junit.Test; + +public class TraversalSelectStepTest { + + @Test + public void shouldObtainPopInstructions() { + TraversalSelectStep step = new TraversalSelectStep(__.select("a").asAdmin(), Pop.first, __.select(Pop.first, "b").select("c")); + + HashSet expectedOutput = TestDataBuilder.createPopInstructionSet( + new Object[]{"b", Pop.first}, + new Object[]{"c", Pop.last} + ); + + assertEquals(step.getPopInstructions(), expectedOutput); + + step = new TraversalSelectStep(__.identity().asAdmin(), Pop.first, __.select(Pop.first, "a", "b", "c")); + + // 3 keys, and Pop.first + expectedOutput = TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.first}, + new Object[]{"c", Pop.first}, + new Object[]{"b", Pop.first} + ); + + assertEquals(step.getPopInstructions(), expectedOutput); + + +} +} \ No newline at end of file diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStepTest.java index 26d145aa056..d14e0b8ad32 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStepTest.java @@ -21,10 +21,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; +import org.junit.Test; import java.util.Arrays; import java.util.List; +import static org.junit.Assert.assertEquals; + /** * @author Daniel Kuppitz (http://gremlin.guru) */ @@ -38,4 +41,11 @@ protected List getTraversals() { // __.property("y", 0) ); } + + @Test + public void testGetPopInstructions() { + final AddPropertyStep step = new AddPropertyStep(__.identity().select("s").asAdmin(), null, "x", 0); + + assertEquals(0, step.getPopInstructions().size()); + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelperTest.java index c7884451ac9..aa86a0d913a 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelperTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelperTest.java @@ -18,10 +18,14 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.util; +import org.apache.tinkerpop.gremlin.TestDataBuilder; import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.TraversalVertexProgramStep; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Pop; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.branch.RepeatStep; @@ -47,14 +51,22 @@ import org.junit.Test; import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.has; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.path; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.repeat; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.union; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.valueMap; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -485,4 +497,85 @@ public void shouldGetStepsOfAssignableClassRecursivelyFromDepthEnsureOrder() { assertEquals("e", ((PropertiesStep) steps.get(4)).getPropertyKeys()[0]); assertEquals("f", ((PropertiesStep) steps.get(5)).getPropertyKeys()[0]); } + + @Test + public void shouldGetPopInstructions() { + final List> traversals = new ArrayList<>(); + final List> expectedResults = new ArrayList<>(); + + /// + traversals.add(__.V().has("person", "name", "marko").as("start").repeat(out().as("reached").select("start")).times(2).select("reached").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"start", Pop.last}, + new Object[]{"reached", Pop.last} + )); + /// + traversals.add(__.V().select("vertex").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"vertex", Pop.last} + )); + /// + traversals.add(__.V().out().as("a").repeat(union(out().select("a"), path().select(Pop.mixed, "b"))).select(Pop.first,"c").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last}, + new Object[]{"b", Pop.mixed}, + new Object[]{"c", Pop.first} + )); + /// + traversals.add(__.V().as("b").repeat(select("b").out().as("a")).times(2).select(Pop.first, "a").select(Pop.last, "a").project("bb").by(__.select(Pop.all, "a")).asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"b", Pop.last}, + new Object[]{"a", Pop.first}, + new Object[]{"a", Pop.all}, + new Object[]{"a", Pop.last} + )); + /// + traversals.add(__.V("1").as("b").repeat(select("b").out().as("a")).times(2).path().as("c").by("name").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"b", Pop.last} + )); + /// + traversals.add(__.V().union(out().as("a"), repeat(out().as("a")).emit()).select(Pop.last, "a").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last} + )); + /// + traversals.add(__.V().has("person", "name", "marko").as("start").repeat(out()).times(2).where(P.neq("start")).values("name").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"start", Pop.last} + )); + /// + traversals.add(__.V().union(out(), repeat(out().as("a")).emit()).select(Pop.last, "a").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last} + )); + /// + traversals.add(__.V().as("a").union(path(), repeat(out().select(Pop.last, "a"))).asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"a", Pop.last} + )); + /// + traversals.add(__.V().hasLabel("person").repeat(out("created")).emit().as("software").select("software").values("name", "lang").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"software", Pop.last} + )); + /// + traversals.add(__.V().hasLabel("person").repeat(out("created").as("created_thing")).emit().as("final").select(Pop.mixed, "created_thing", "final"). by("name").by("lang").asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"created_thing", Pop.mixed}, + new Object[]{"final", Pop.mixed} + )); + /// + traversals.add(__.V().has("person", "name", "marko").as("start").repeat(out().as("path_element")).until(has("lang")).as("software").select("start", "path_element", "software").by("name").by("name").by(valueMap("name", "lang")).asAdmin()); + expectedResults.add(TestDataBuilder.createPopInstructionSet( + new Object[]{"start", Pop.last}, + new Object[]{"path_element", Pop.last}, + new Object[]{"software", Pop.last} + )); + + // Run all the tests + for (int i = 0; i < traversals.size(); i++) { + assertEquals(TraversalHelper.getPopInstructions(traversals.get(i)), expectedResults.get(i)); + } + } }