You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/03/26 14:41:40 UTC

incubator-tinkerpop git commit: DedupGlobalStep can now do scopedKey deduping and by()-modulation within the star-graph. Added a test case that verifies by()-modulating working and opened up two test cases that used scopedKeys. Able to remove two 'chunks

Repository: incubator-tinkerpop
Updated Branches:
  refs/heads/master 34309809e -> fb10f63e5


DedupGlobalStep can now do scopedKey deduping and by()-modulation within the star-graph. Added a test case that verifies by()-modulating working and opened up two test cases that used scopedKeys. Able to remove two 'chunks' of checks in ComputerVerificationStrategy -- its getting smaller and smaller.... Made the error messages in ComputerVerificationStrategy more clear. Simple update -- CTR.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/fb10f63e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/fb10f63e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/fb10f63e

Branch: refs/heads/master
Commit: fb10f63e5708ece621f248e72b9af36b106ea028
Parents: 3430980
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Sat Mar 26 07:41:32 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Sat Mar 26 07:41:32 2016 -0600

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../gremlin/process/traversal/Operator.java     |  6 ++-
 .../traversal/step/filter/DedupGlobalStep.java  | 54 ++++++++++++++------
 .../step/filter/WhereTraversalStep.java         | 11 +++-
 .../ComputerVerificationStrategy.java           | 24 +++------
 .../step/filter/GroovyDedupTest.groovy          |  5 ++
 .../traversal/step/filter/DedupTest.java        | 33 +++++++++++-
 .../structure/TinkerGraphPlayTest.java          |  2 +-
 8 files changed, 95 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index f55cd30..10084d6 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/incubator-tinkerpop/master/docs/
 TinkerPop 3.2.0 (NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* `DedupGlobalStep` can now handle star-bound `by()`-modulators and scoped keys on `GraphComputer`.
 * Added `Computer` which is a builder for `GraphComputers` that is serializable.
 * `PersistedOutputRDD` now implements `PersistResultGraphAware` and thus, no more unneeded warnings when using it.
 * Renamed `StandardTraversalMetrics` to `DefaultTraversalMetrics` given the `DefaultXXX`-convention throughout. (*breaking*)

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
index 2cf5d82..a52f34b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Operator.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.process.traversal;
 
 import java.util.Collection;
+import java.util.Map;
 import java.util.function.BinaryOperator;
 
 /**
@@ -73,7 +74,10 @@ public enum Operator implements BinaryOperator<Object> {
     },
     addAll {
         public Object apply(final Object a, final Object b) {
-            ((Collection) a).addAll((Collection) b);
+            if (a instanceof Map)
+                ((Map) a).putAll((Map) b);
+            else
+                ((Collection) a).addAll((Collection) b);
             return a;
         }
     },

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
----------------------------------------------------------------------
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 546d24e..9336a1a 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
@@ -25,11 +25,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Barrier;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
-import org.apache.tinkerpop.gremlin.process.traversal.step.Bypassing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
 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.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -39,8 +39,10 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.function.BinaryOperator;
@@ -48,11 +50,11 @@ import java.util.function.BinaryOperator;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class DedupGlobalStep<S> extends FilterStep<S> implements TraversalParent, Scoping, Bypassing, Barrier<TraverserSet<S>>, ByModulating {
+public final class DedupGlobalStep<S> extends FilterStep<S> implements TraversalParent, Scoping, GraphComputing, Barrier<Map<Object, Traverser.Admin<S>>>, ByModulating, PathProcessor {
 
     private Traversal.Admin<S, Object> dedupTraversal = null;
     private Set<Object> duplicateSet = new HashSet<>();
-    private boolean bypass = false;
+    private boolean onGraphComputer = false;
     private final Set<String> dedupLabels;
 
     public DedupGlobalStep(final Traversal.Admin traversal, final String... dedupLabels) {
@@ -62,7 +64,7 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
 
     @Override
     protected boolean filter(final Traverser.Admin<S> traverser) {
-        if (this.bypass) return true;
+        if (this.onGraphComputer) return true;
         traverser.setBulk(1);
         if (null == this.dedupLabels) {
             return this.duplicateSet.add(TraversalUtil.applyNullable(traverser, this.dedupTraversal));
@@ -74,6 +76,11 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
     }
 
     @Override
+    public ElementRequirement getMaxRequirement() {
+        return null == this.dedupLabels ? ElementRequirement.ID : PathProcessor.super.getMaxRequirement();
+    }
+
+    @Override
     public List<Traversal<S, Object>> getLocalChildren() {
         return null == this.dedupTraversal ? Collections.emptyList() : Collections.singletonList(this.dedupTraversal);
     }
@@ -127,8 +134,8 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
     }
 
     @Override
-    public void setBypass(final boolean bypass) {
-        this.bypass = bypass;
+    public void onGraphComputer() {
+        this.onGraphComputer = true;
     }
 
     @Override
@@ -147,30 +154,43 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
     }
 
     @Override
-    public TraverserSet<S> nextBarrier() throws NoSuchElementException {
-        final TraverserSet<S> traverserSet = new TraverserSet<>();
+    public Map<Object, Traverser.Admin<S>> nextBarrier() throws NoSuchElementException {
+        final Map<Object, Traverser.Admin<S>> map = new HashMap<>();
         while (this.starts.hasNext()) {
             final Traverser.Admin<S> traverser = this.starts.next();
-            traverser.set(DetachedFactory.detach(traverser.get(), true));
-            traverserSet.add(traverser);
+            final Object object;
+            if (null != this.dedupLabels) {
+                object = new ArrayList<>(this.dedupLabels.size());
+                for (final String label : this.dedupLabels) {
+                    ((List) object).add(TraversalUtil.applyNullable((S) this.getScopeValue(Pop.last, label, traverser), this.dedupTraversal));
+                }
+            } else {
+                object = TraversalUtil.applyNullable(traverser, this.dedupTraversal);
+            }
+            if (!map.containsKey(object)) {
+                traverser.setBulk(1l);
+                traverser.set(DetachedFactory.detach(traverser.get(), true));
+                map.put(object, traverser);
+            }
         }
-        if (traverserSet.isEmpty())
+        if (map.isEmpty())
             throw FastNoSuchElementException.instance();
         else
-            return traverserSet;
+            return map;
+
     }
 
     @Override
-    public void addBarrier(final TraverserSet<S> barrier) {
-        IteratorUtils.removeOnNext(barrier.iterator()).forEachRemaining(traverser -> {
+    public void addBarrier(final Map<Object, Traverser.Admin<S>> barrier) {
+        IteratorUtils.removeOnNext(barrier.entrySet().iterator()).forEachRemaining(entry -> {
+            final Traverser.Admin<S> traverser = entry.getValue();
             traverser.setSideEffects(this.getTraversal().getSideEffects());
             this.addStart(traverser);
         });
     }
 
     @Override
-    public MemoryComputeKey<TraverserSet<S>> getMemoryComputeKey() {
+    public MemoryComputeKey<Map<Object, Traverser.Admin<S>>> getMemoryComputeKey() {
         return MemoryComputeKey.of(this.getId(), (BinaryOperator) Operator.addAll, false, true);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
----------------------------------------------------------------------
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 e5d1f35..388104d 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
@@ -22,10 +22,10 @@ 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.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
 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.map.MapStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.ProfileSideEffectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ProfileStep;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
@@ -42,7 +42,7 @@ import java.util.Set;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class WhereTraversalStep<S> extends FilterStep<S> implements TraversalParent, Scoping {
+public final class WhereTraversalStep<S> extends FilterStep<S> implements TraversalParent, Scoping, PathProcessor {
 
     protected Traversal.Admin<?, ?> whereTraversal;
     protected final Set<String> scopeKeys = new HashSet<>();
@@ -81,6 +81,13 @@ public final class WhereTraversalStep<S> extends FilterStep<S> implements Traver
         }
     }
 
+    @Override
+    public ElementRequirement getMaxRequirement() {
+        return TraversalHelper.getVariableLocations(this.whereTraversal).contains(Scoping.Variable.START) ?
+                PathProcessor.super.getMaxRequirement() :
+                ElementRequirement.ID;
+    }
+
 
     @Override
     protected boolean filter(final Traverser.Admin<S> traverser) {

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
index 4087f08..fc73fc3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
@@ -27,10 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
-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.filter.DedupGlobalStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphStep;
@@ -71,7 +68,7 @@ public final class ComputerVerificationStrategy extends AbstractTraversalStrateg
             if (TraversalHelper.getStepsOfAssignableClassRecursively(GraphStep.class, traversal).size() > 1)
                 throw new VerificationException("Mid-traversal V()/E() is currently not supported on GraphComputer", traversal);
             if (TraversalHelper.hasStepOfAssignableClassRecursively(ProfileStep.class, traversal) && TraversalHelper.getStepsOfAssignableClass(VertexProgramStep.class, TraversalHelper.getRootTraversal(traversal)).size() > 1)
-                throw new VerificationException("Profiling a multi-GraphComputer traversal is currently not supported", traversal);
+                throw new VerificationException("Profiling a multi-VertexProgramStep traversal is currently not supported on GraphComputer", traversal);
         }
 
         for (final Step<?, ?> step : traversal.getSteps()) {
@@ -86,30 +83,21 @@ public final class ComputerVerificationStrategy extends AbstractTraversalStrateg
                         .filter(t -> !TraversalHelper.isLocalStarGraph(t.asAdmin()))
                         .findAny();
                 if (traversalOptional.isPresent())
-                    throw new VerificationException("Local traversals on GraphComputer may not traverse past the local star-graph: " + traversalOptional.get(), traversal);
+                    throw new VerificationException("Local traversals may not traverse past the local star-graph on GraphComputer: " + traversalOptional.get(), traversal);
             }
 
             // collecting barriers and dedup global use can only operate on the element and its properties (no incidences)
-            if ((step instanceof CollectingBarrierStep || step instanceof DedupGlobalStep) && step instanceof TraversalParent) {
+            if (step instanceof CollectingBarrierStep && step instanceof TraversalParent) {
                 if (((TraversalParent) step).getLocalChildren().stream().filter(t -> !TraversalHelper.isLocalProperties(t)).findAny().isPresent())
-                    throw new VerificationException("A barrier-steps can not process the incident edges of a vertex on GraphComputer: " + step, traversal);
-            }
-
-            // this is due to how reducing works and can be fixed by generalizing the current simple model
-            // we need to make it so dedup global does its project post reduction (easy to do, just do it)
-            if ((step instanceof DedupGlobalStep) && (!((DedupGlobalStep) step).getScopeKeys().isEmpty())) {
-                throw new VerificationException("A dedup()-step can not process scoped elements on GraphComputer: " + step, traversal);
+                    throw new VerificationException("The following barrier step can not process the incident edges of a vertex on GraphComputer: " + step, traversal);
             }
 
             // this is a problem because sideEffect.merge() is transient on the OLAP reduction
             if (TraversalHelper.getRootTraversal(traversal).getTraverserRequirements().contains(TraverserRequirement.ONE_BULK))
-                throw new VerificationException("One bulk us currently not supported on GraphComputer: " + step, traversal);
-
-            if ((step instanceof WhereTraversalStep && TraversalHelper.getVariableLocations(((WhereTraversalStep<?>) step).getLocalChildren().get(0)).contains(Scoping.Variable.START)))
-                throw new VerificationException("A where()-step that has a start variable is not allowed on GraphComputer because the variable value is retrieved from the path: " + step, traversal);
+                throw new VerificationException("One bulk is currently not supported on GraphComputer: " + step, traversal);
 
             if (step instanceof PathProcessor && ((PathProcessor) step).getMaxRequirement() != PathProcessor.ElementRequirement.ID)
-                throw new VerificationException("The following path processor step requires more than the element id on GraphComputer: " + step + " requires " + ((PathProcessor) step).getMaxRequirement(), traversal);
+                throw new VerificationException("It is not possible to access more than a path element's id on GraphComputer: " + step + " requires " + ((PathProcessor) step).getMaxRequirement(), traversal);
 
             if (UNSUPPORTED_STEPS.stream().filter(c -> c.isAssignableFrom(step.getClass())).findFirst().isPresent())
                 throw new VerificationException("The following step is currently not supported on GraphComputer: " + step, traversal);

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/filter/GroovyDedupTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/filter/GroovyDedupTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/filter/GroovyDedupTest.groovy
index 02de286..86c3e15 100644
--- a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/filter/GroovyDedupTest.groovy
+++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/filter/GroovyDedupTest.groovy
@@ -80,5 +80,10 @@ public abstract class GroovyDedupTest {
         Traversal<Vertex, String> get_g_V_outE_asXeX_inV_asXvX_selectXeX_order_byXweight_incrX_selectXvX_valuesXnameX_dedup() {
             new ScriptTraversal<>(g, "gremlin-groovy", "g.V.outE.as('e').inV.as('v').select('e').order.by('weight', incr).select('v').values('name').dedup")
         }
+
+        @Override
+        public Traversal<Vertex, String> get_g_V_both_both_dedup_byXoutE_countX_name() {
+            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.both.both.dedup.by(outE().count).name")
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
index 3a6c6ed..fe1db71 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
@@ -24,16 +24,24 @@ import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -63,6 +71,8 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, String> get_g_V_outE_asXeX_inV_asXvX_selectXeX_order_byXweight_incrX_selectXvX_valuesXnameX_dedup();
 
+    public abstract Traversal<Vertex, String> get_g_V_both_both_dedup_byXoutE_countX_name();
+
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_both_dedup_name() {
@@ -215,6 +225,20 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
         assertFalse(traversal.hasNext());
     }
 
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_both_both_dedup_byXoutE_countX_name() {
+        final Traversal<Vertex, String> traversal = get_g_V_both_both_dedup_byXoutE_countX_name();
+        printTraversalForm(traversal);
+        final List<String> names = traversal.toList();
+        assertEquals(4, names.size());
+        assertTrue(names.contains("josh"));
+        assertTrue(names.contains("peter"));
+        assertTrue(names.contains("marko"));
+        // the 4th is vadas, ripple, or lop
+        assertEquals(4, new HashSet<>(names).size());
+    }
+
     public static class Traversals extends DedupTest {
         @Override
         public Traversal<Vertex, String> get_g_V_both_dedup_name() {
@@ -265,5 +289,10 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, String> get_g_V_outE_asXeX_inV_asXvX_selectXeX_order_byXweight_incrX_selectXvX_valuesXnameX_dedup() {
             return g.V().outE().as("e").inV().as("v").select("e").order().by("weight", Order.incr).select("v").<String>values("name").dedup();
         }
+
+        @Override
+        public Traversal<Vertex, String> get_g_V_both_both_dedup_byXoutE_countX_name() {
+            return g.V().both().both().dedup().by(__.outE().count()).values("name");
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/fb10f63e/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 8202055..547c820 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
@@ -70,7 +70,7 @@ public class TinkerGraphPlayTest {
         //System.out.println(g.V().outE("knows").identity().inV().count().is(P.eq(5)).explain());
         //System.out.println(g.V().hasLabel("person").fold().order(Scope.local).by("age").toList());
         //System.out.println(g.V().repeat(out()).times(2).profile("m").explain());
-        final Traversal<?, ?> traversal = g.V().repeat(__.union(__.<Vertex>identity().as("a"), out())).times(2).profile();
+        final Traversal<?, ?> traversal = g.V().dedup().by(__.outE().count()).values("name");
         //System.out.println(g.V().hasLabel("person").pageRank().by("rank").by(bothE()).values("rank").profile("m").explain());
         //System.out.println(traversal.asAdmin().clone().toString());
         // final Traversal<?,?> clone = traversal.asAdmin().clone();