You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by dk...@apache.org on 2017/01/13 11:42:11 UTC

[4/4] tinkerpop git commit: added DedupCountStrategy which, in OLAP, will combine dedup().count() patterns into a single step and thus, limits the amount of traverser migration at the master traversal post-dedup barrier. The speed up is not bad, but also

added DedupCountStrategy which, in OLAP, will combine dedup().count() patterns into a single step and thus, limits the amount of traverser migration at the master traversal post-dedup barrier. The speed up is not bad, but also not great. 2900ms vs. 3150ms on a benchmark dataset.


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

Branch: refs/heads/DEDUPCOUNT
Commit: aebcbc35b4f8fc66e5b1af5a2d733ae9c485f2ee
Parents: c66424d
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Wed Jan 11 07:51:23 2017 -0700
Committer: Daniel Kuppitz <da...@hotmail.com>
Committed: Fri Jan 13 12:41:47 2017 +0100

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 .../process/traversal/TraversalStrategies.java  |   2 +
 .../step/map/DedupCountGlobalStep.java          | 132 +++++++++++++++++++
 .../optimization/DedupCountStrategy.java        |  56 ++++++++
 .../optimization/DedupCountStrategyTest.java    |  99 ++++++++++++++
 .../step/filter/GroovyDedupTest.groovy          |  10 ++
 .../traversal/step/filter/DedupTest.java        |  32 +++++
 7 files changed, 332 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 4ca45c6..1fd2ca7 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Added `DedupCountStrategy` and `DedupCountGlobalStep` which are used in OLAP to combine `dedup().count()` patterns into a single step.
 * `PathRetractionStrategy` does not add a `NoOpBarrierStep` to the end of local children as its wasted computation in 99% of traversals.
 * Fixed a bug in `AddVertexStartStep` where if a side-effect was being used in the parametrization, an NPE occurred.
 * Fixed a bug in `LazyBarrierStrategy` where `profile()` was deactivating it accidentally.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index 015df70..2d42a1b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimiza
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.DedupCountStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy;
@@ -223,6 +224,7 @@ public interface TraversalStrategies extends Serializable, Cloneable {
             graphComputerStrategies.addStrategies(
                     GraphFilterStrategy.instance(),
                     OrderLimitStrategy.instance(),
+                    DedupCountStrategy.instance(),
                     PathProcessorStrategy.instance(),
                     ComputerVerificationStrategy.instance());
             GRAPH_COMPUTER_CACHE.put(GraphComputer.class, graphComputerStrategies);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DedupCountGlobalStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DedupCountGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DedupCountGlobalStep.java
new file mode 100644
index 0000000..1669392
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DedupCountGlobalStep.java
@@ -0,0 +1,132 @@
+/*
+ *  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 org.apache.tinkerpop.gremlin.process.traversal.Operator;
+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.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.util.ReducingBarrierStep;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.HashSetSupplier;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class DedupCountGlobalStep<S> extends ReducingBarrierStep<S, Object> implements TraversalParent, Scoping {
+
+    private Traversal.Admin<S, Object> dedupTraversal;
+    private final Set<String> dedupLabels;
+
+    public DedupCountGlobalStep(final Traversal.Admin traversal, final DedupGlobalStep<S> dedupGlobalStep) {
+        super(traversal);
+        this.dedupLabels = dedupGlobalStep.getScopeKeys().isEmpty() ? null : dedupGlobalStep.getScopeKeys();
+        this.dedupTraversal = dedupGlobalStep.getLocalChildren().isEmpty() ? null : (Traversal.Admin<S, Object>) dedupGlobalStep.getLocalChildren().get(0);
+        this.labels.addAll(dedupGlobalStep.getNextStep().getLabels());
+        this.setSeedSupplier((Supplier) HashSetSupplier.instance());
+        this.setReducingBiOperator(Operator.addAll);
+    }
+
+    @Override
+    public Traverser.Admin<Object> processNextStart() {
+        final Traverser.Admin<Object> traverser = super.processNextStart();
+        traverser.set((long) ((Set) traverser.get()).size());
+        return traverser;
+    }
+
+    @Override
+    public List<Traversal<S, Object>> getLocalChildren() {
+        return null == this.dedupTraversal ? Collections.emptyList() : Collections.singletonList(this.dedupTraversal);
+    }
+
+    public void setDedupTraversal(final Traversal.Admin<S, Object> dedupTraversal) {
+        this.dedupTraversal = dedupTraversal;
+    }
+
+    @Override
+    public DedupCountGlobalStep<S> clone() {
+        final DedupCountGlobalStep<S> clone = (DedupCountGlobalStep<S>) super.clone();
+        if (null != this.dedupTraversal)
+            clone.dedupTraversal = this.dedupTraversal.clone();
+        return clone;
+    }
+
+    @Override
+    public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
+        super.setTraversal(parentTraversal);
+        this.integrateChild(this.dedupTraversal);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        if (this.dedupTraversal != null)
+            result ^= this.dedupTraversal.hashCode();
+        if (this.dedupLabels != null)
+            result ^= this.dedupLabels.hashCode();
+        return result;
+    }
+
+    @Override
+    public Object projectTraverser(final Traverser.Admin<S> 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);
+        ///
+        final Set<Object> set = new HashSet<>();
+        set.add(object);
+        return set;
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.stepString(this, this.dedupLabels, this.dedupTraversal);
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return this.dedupLabels == null ?
+                this.getSelfAndChildRequirements(TraverserRequirement.BULK) :
+                this.getSelfAndChildRequirements(TraverserRequirement.LABELED_PATH, TraverserRequirement.BULK);
+    }
+
+    @Override
+    public Set<String> getScopeKeys() {
+        return null == this.dedupLabels ? Collections.emptySet() : this.dedupLabels;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategy.java
new file mode 100644
index 0000000..02655eb
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategy.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 and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.DedupCountGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class DedupCountStrategy extends AbstractTraversalStrategy<TraversalStrategy.OptimizationStrategy> implements TraversalStrategy.OptimizationStrategy {
+
+    private static final DedupCountStrategy INSTANCE = new DedupCountStrategy();
+
+    private DedupCountStrategy() {
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        if (TraversalHelper.onGraphComputer(traversal) && TraversalHelper.isGlobalChild(traversal)) {
+            for (final DedupGlobalStep<Object> step : TraversalHelper.getStepsOfClass(DedupGlobalStep.class, traversal)) {
+                if (step.getNextStep() instanceof CountGlobalStep) {
+                    final CountGlobalStep countStep = (CountGlobalStep) step.getNextStep();
+                    TraversalHelper.replaceStep(step, new DedupCountGlobalStep<>(traversal, step), traversal);
+                    traversal.removeStep(countStep);
+                }
+            }
+        }
+    }
+
+    public static DedupCountStrategy instance() {
+        return INSTANCE;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategyTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategyTest.java
new file mode 100644
index 0000000..30d3b8e
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/DedupCountStrategyTest.java
@@ -0,0 +1,99 @@
+/*
+ *  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.strategy.optimization;
+
+import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.TraversalVertexProgramStep;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+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.lambda.ElementValueTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.DedupCountGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.dedup;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+
+@RunWith(Parameterized.class)
+public class DedupCountStrategyTest {
+
+    @Parameterized.Parameter(value = 0)
+    public Traversal original;
+
+    @Parameterized.Parameter(value = 1)
+    public Traversal optimized;
+
+    @Test
+    public void doTest() {
+        this.original.asAdmin().setParent(new TraversalVertexProgramStep(EmptyTraversal.instance(), EmptyTraversal.instance())); // trick it
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(DedupCountStrategy.instance());
+        this.original.asAdmin().setStrategies(strategies);
+        this.original.asAdmin().applyStrategies();
+
+        assertEquals(this.optimized, this.original);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object[]> generateTestParameters() {
+
+        return Arrays.asList(new Traversal[][]{
+                {out().count().dedup(), out().count().dedup()},
+                {dedup().count(), addDedupCount(__.start())},
+                {out().dedup().count(), addDedupCount(out())},
+                {out().dedup().count().as("a"), addDedupCount(out()).as("a")},
+                {out().dedup().as("a").count().as("b", "c"), addDedupCount(out()).as("b", "c")},
+                {out().dedup("a", "b").count(), addDedupCount(out(), Arrays.asList("a", "b"))},
+                {out().dedup("a", "b").by("name").count(), addDedupCount(out(), Arrays.asList("a", "b"), new ElementValueTraversal("name"))},
+                {out().dedup("a", "b").by(out("knows").count()).count(), addDedupCount(out(), Arrays.asList("a", "b"), __.out("knows").count())},
+        });
+    }
+
+    private static GraphTraversal.Admin<?, ?> addDedupCount(final GraphTraversal<?, ?> traversal, final Collection<String> dedupLabels, final Traversal dedupTraversal) {
+        final DedupCountGlobalStep<?> step = new DedupCountGlobalStep<>((Traversal.Admin) traversal, new DedupGlobalStep<>(traversal.asAdmin(), dedupLabels.toArray(new String[dedupLabels.size()])));
+        if (!(dedupTraversal instanceof EmptyTraversal))
+            step.setDedupTraversal(dedupTraversal.asAdmin());
+        return traversal.asAdmin().addStep(step);
+    }
+
+    private static GraphTraversal.Admin<?, ?> addDedupCount(final GraphTraversal<?, ?> traversal) {
+        return addDedupCount(traversal, Collections.emptyList(), EmptyTraversal.instance());
+    }
+
+    private static GraphTraversal.Admin<?, ?> addDedupCount(final GraphTraversal<?, ?> traversal, final Collection<String> dedupLabels) {
+        return addDedupCount(traversal, dedupLabels, EmptyTraversal.instance());
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/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 a041fdb..6de69c9 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
@@ -109,5 +109,15 @@ public abstract class GroovyDedupTest {
         public Traversal<Vertex, Long> get_g_V_repeatXdedupX_timesX2X_count() {
             new ScriptTraversal<>(g, "gremlin-groovy", "g.V.repeat(dedup()).times(2).count")
         }
+
+        @Override
+        public Traversal<Vertex, Long> get_g_V_outXcreatedX_dedup_count() {
+            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.out('created').dedup.count")
+        }
+
+        @Override
+        public Traversal<Vertex, Long> get_g_V_asXaX_outXcreatedX_inXcreatedX_asXbX_dedupXa_bX_byXidX_count() {
+            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.as('a').out('created').in('created').as('b').dedup('a', 'b').by(id).count")
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/aebcbc35/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 5ac5e22..3791a66 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
@@ -91,6 +91,10 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Long> get_g_V_repeatXdedupX_timesX2X_count();
 
+    public abstract Traversal<Vertex, Long> get_g_V_asXaX_outXcreatedX_inXcreatedX_asXbX_dedupXa_bX_byXidX_count();
+
+    public abstract Traversal<Vertex, Long> get_g_V_outXcreatedX_dedup_count();
+
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_out_in_valuesXnameX_fold_dedupXlocalX_unfold() {
@@ -312,6 +316,24 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
         assertFalse(traversal.hasNext());
     }
 
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_outXcreatedX_dedup_count() {
+        final Traversal<Vertex, Long> traversal = get_g_V_outXcreatedX_dedup_count();
+        printTraversalForm(traversal);
+        assertEquals(2L, traversal.next().longValue());
+        assertFalse(traversal.hasNext());
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_asXaX_outXcreatedX_inXcreatedX_asXbX_dedupXa_bX_byXidX_count() {
+        final Traversal<Vertex, Long> traversal = get_g_V_asXaX_outXcreatedX_inXcreatedX_asXbX_dedupXa_bX_byXidX_count();
+        printTraversalForm(traversal);
+        assertEquals(9L, traversal.next().longValue());
+        assertFalse(traversal.hasNext());
+    }
+
 
     public static class Traversals extends DedupTest {
         @Override
@@ -393,5 +415,15 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Long> get_g_V_repeatXdedupX_timesX2X_count() {
             return g.V().repeat(dedup()).times(2).count();
         }
+
+        @Override
+        public Traversal<Vertex, Long> get_g_V_outXcreatedX_dedup_count() {
+            return g.V().out("created").dedup().count();
+        }
+
+        @Override
+        public Traversal<Vertex, Long> get_g_V_asXaX_outXcreatedX_inXcreatedX_asXbX_dedupXa_bX_byXidX_count() {
+            return g.V().as("a").out("created").in("created").as("b").dedup("a", "b").by(T.id).count();
+        }
     }
 }