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

[01/24] tinkerpop git commit: Added more cycle detection and traversal induced value recipes. [Forced Update!]

Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1600 0fcb306ef -> 866e96efa (forced update)


Added more cycle detection and traversal induced value recipes.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 6dba5ec636aed6fd3eeb1ac7db7b4043657689b1
Parents: 9c44f0d
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jan 6 08:57:31 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jan 6 08:57:31 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/cycle-detection.asciidoc       | 55 ++++++++++++++++++
 .../recipes/traversal-induced-values.asciidoc   | 60 ++++++++++++++++++++
 2 files changed, 115 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6dba5ec6/docs/src/recipes/cycle-detection.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/cycle-detection.asciidoc b/docs/src/recipes/cycle-detection.asciidoc
index 1a8650b..7cebb00 100644
--- a/docs/src/recipes/cycle-detection.asciidoc
+++ b/docs/src/recipes/cycle-detection.asciidoc
@@ -58,4 +58,59 @@ arbitrary length over both incoming and outgoing edges in the modern graph?
 g.V().as('a').repeat(both().simplePath()).emit(loops().is(gt(1))).
   both().where(eq('a')).path().
   dedup().by(unfold().order().by(id).dedup().fold())
+----
+
+An interesting type of cycle is known as the Eulerian circuit which is a path taken in a graph where each edge is
+visited once and the path starts and ends with the same vertex. Consider the following graph, representative of an
+imaginary but geographically similar link:https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg[K�nigsberg]
+that happens to have an eighth bridge (the diagram depicts edge direction but direction won't be considered in the traversal):
+
+image:eulerian-circuit.png[width=500]
+
+Gremlin can detect if such a cycle exists with:
+
+[gremlin-groovy]
+----
+g.addV().property(id, 'blue').as('b').
+  addV().property(id, 'orange').as('o').
+  addV().property(id, 'red').as('r').
+  addV().property(id, 'green').as('g').
+  addE('bridge').from('g').to('b').
+  addE('bridge').from('g').to('o').
+  addE('bridge').from('g').to('r').
+  addE('bridge').from('g').to('r').
+  addE('bridge').from('o').to('b').
+  addE('bridge').from('o').to('b').
+  addE('bridge').from('o').to('r').
+  addE('bridge').from('o').to('r').iterate()
+g.V().sideEffect(outE("bridge").aggregate("bridges")).barrier().    <1>
+  repeat(bothE().                                                   <2>
+         or(__.not(select('e')),
+            __.not(filter(__.as('x').select(all, 'e').unfold().     <3>
+                   where(eq('x'))))).as('e').
+         otherV()).
+    until(select(all, 'e').count(local).as("c").                    <4>
+          select("bridges").count(local).where(eq("c"))).hasNext()
+----
+
+<1> Gather all the edges in a "bridges" side effect.
+<2> As mentioned earlier with the diagram, directionality is ignored as the traversal uses `bothE` and, later, `otherV`.
+<3> In continually traversing over both incoming and outgoing edges, this path is only worth continuing if the edges
+traversed thus far are only traversed once. That set of edges is maintained in "e".
+<4> The traversal should repeat until the number of edges traversed in "e" is equal to the total number gathered in
+the first step above, which would mean that the complete circuit has been made.
+
+Unlike K�nigsberg, with just seven bridges, a Eulerian circuit exists in the case with an eighth bridge. The first
+detected circuit can be displayed with:
+
+[gremlin-groovy,existing]
+----
+g.V().sideEffect(outE("bridge").aggregate("bridges")).barrier().
+  repeat(bothE().or(__.not(select('e')),
+                    __.not(filter(__.as('x').select(all, 'e').unfold().
+                           where(eq('x'))))).as('e').otherV()).
+    until(select(all, 'e').count(local).as("c").
+          select("bridges").count(local).where(eq("c"))).limit(1).
+  path().by(id).by(constant(" -> ")).
+  map {String.join("", it.get().objects())}
 ----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6dba5ec6/docs/src/recipes/traversal-induced-values.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/traversal-induced-values.asciidoc b/docs/src/recipes/traversal-induced-values.asciidoc
index 5f48591..e292e16 100644
--- a/docs/src/recipes/traversal-induced-values.asciidoc
+++ b/docs/src/recipes/traversal-induced-values.asciidoc
@@ -64,4 +64,64 @@ of one `Vertex` to another:
 g.V().has('name', 'marko').as('marko').
   out('created').property('creator', select('marko').by('name'))
 g.V().has('name', 'marko').out('created').valueMap()
+----
+
+In a more complex example of how this might work, consider a situation where the goal is to propagate a value stored on
+a particular vertex through one of more additional connected vertices using some value on the connecting edges to
+determine the value to assign. For example, the following graph depicts three "tank" vertices where the edges represent
+the direction a particular "tank" should drain and the "factor" by which it should do it:
+
+image:traversal-induced-values-1.png[width=700]
+
+If the traversal started at tank "a", then the value of "amount" on that tank would be used to calculate what the value
+of tank "b" was by multiplying it by the value of the "factor" property on the edge between vertices "a" and "b". In
+this case the amount of tank "b" would then be 50. Following this pattern, when going from tank "b" to tank "c", the
+value of the "amount" of tank "c" would be 5.
+
+image:traversal-induced-values-2.png[width=700]
+
+Using Gremlin `sack()`, this kind of operation could be specified as a single traversal:
+
+[gremlin-groovy]
+----
+g.addV(label, 'tank', 'name', 'a', 'amount', 100.0).as('a').
+  addV(label, 'tank', 'name', 'b', 'amount', 0.0).as('b').
+  addV(label, 'tank', 'name', 'c', 'amount', 0.0).as('c').
+  addE('drain').property('factor', 0.5).from('a').to('b').
+  addE('drain').property('factor', 0.1).from('b').to('c').iterate()
+a = g.V().has('name','a').next()
+g.withSack(a.value('amount')).
+  V(a).repeat(outE('drain').sack(mult).by('factor').
+              inV().property('amount', sack())).
+       until(__.outE('drain').count().is(0)).iterate()
+g.V().valueMap()
+----
+
+The "sack value" gets initialized to the value of tank "a". The traversal iteratively traverses out on the "drain"
+edges and uses `mult` to multiply the sack value by the value of "factor". The sack value at that point is then
+written to the "amount" of the current vertex.
+
+As shown in the previous example, `sack()` is a useful way to "carry" and manipulate a value that can be later used
+elsewhere in the traversal. Here is another example of its usage where it is utilized to increment all the "age" values
+in the modern toy graph by 10:
+
+[gremlin-groovy,modern]
+----
+g.withSack(0).V().has("age").
+  sack(assign).by("age").sack(sum).by(constant(10)).
+  property("age", sack()).valueMap()
+----
+
+In the above case, the sack is initialized to zero and as each vertex is iterated, the "age" is assigned to the sack
+with `sack(assign).by('age')`. That value in the sack is then incremented by the value `constant(10)` and assigned to
+the "age" property of the same vertex.
+
+This value the sack is incremented by need not be a constant. It could also be derived from the traversal itself.
+Using the same example, the "weight" property on the incident edges will be used as the value to add to the sack:
+
+[gremlin-groovy,modern]
+----
+g.withSack(0).V().has("age").
+  sack(assign).by("age").sack(sum).by(bothE().values("weight").sum()).
+  property("age", sack()).valueMap()
 ----
\ No newline at end of file


[14/24] tinkerpop git commit: Added some additional examples to appendix recipes.

Posted by sp...@apache.org.
Added some additional examples to appendix recipes.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 4be8a1b2ec8c15aee1c84199e560687a1e3fdf39
Parents: 0fbfd75
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Jan 12 08:06:07 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jan 12 08:06:07 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc       | 23 +++++++++++++++++++++++
 docs/src/recipes/duplicate-edge.asciidoc | 18 ++++++++++++++++++
 2 files changed, 41 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4be8a1b2/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 9038cea..9d0ea7b 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -43,6 +43,29 @@ g.V().as('p').
                by(count(local)).by()).next()
 ----
 
+It might also be alternatively written as:
+
+[gremlin-groovy,existing]
+----
+g.V().group().
+        by('name').
+        by(project('numFollowers','followers').
+             by(__.in('follows').count()).
+             by(__.in('follows').values('name').fold())).next()
+----
+
+or even:
+
+[gremlin-groovy,existing]
+----
+g.V().group().
+        by('name').
+        by(__.in('follows').values('name').fold().
+            project('numFollowers','followers').
+              by(count(local)).
+              by()).next()
+----
+
 [[appendix-b]]
 _In the "modern" graph, show each person, the software they worked on and the co-worker count for the software and
 the names of those co-workers._

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4be8a1b2/docs/src/recipes/duplicate-edge.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/duplicate-edge.asciidoc b/docs/src/recipes/duplicate-edge.asciidoc
index ed56106..a62716e 100644
--- a/docs/src/recipes/duplicate-edge.asciidoc
+++ b/docs/src/recipes/duplicate-edge.asciidoc
@@ -86,6 +86,24 @@ the outgoing vertex, the edge label, and the incoming vertex as the key, with th
 value.
 <4> The rest of the traversal is the same as the previous one.
 
+Note that the above traversal could also be written using `match` step:
+
+[gremlin-groovy,existing]
+----
+g.V().match(
+    __.as("ov").outE().as("e"),
+    __.as("e").inV().as("iv"),
+    __.as("iv").inE().as("ie"),
+    __.as("ie").outV().as("ov")).
+      where("ie",neq("e")).
+      where("ie",eq("e")).by(label).
+    select("ie").
+    group().
+      by(select("ov","e","iv").by().by(label)).
+    unfold().select(values).
+      where(count(local).is(gt(1)))
+----
+
 A third way to approach this problem would be to force a link:https://en.wikipedia.org/wiki/Depth-first_search[depth-first search].
 The previous examples invoke traversal strategies that force a link:https://en.wikipedia.org/wiki/Breadth-first_search[breadth first search]
 as a performance optimization.


[04/24] tinkerpop git commit: Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating steps.

Posted by sp...@apache.org.
Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating steps.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 52e3b86c06938c3427b2452c39a44b75d92e5a6f
Parents: 7ba7ecd
Author: Daniel Kuppitz <da...@hotmail.com>
Authored: Mon Jan 9 15:31:54 2017 +0100
Committer: Daniel Kuppitz <da...@hotmail.com>
Committed: Mon Jan 9 15:31:54 2017 +0100

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../IncidentToAdjacentStrategy.java             | 50 +++++++++--------
 .../IncidentToAdjacentStrategyTest.java         | 56 ++++++++++----------
 3 files changed, 58 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/52e3b86c/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index fa67c56..f82fa23 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.1.6 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating steps.
 * Returned a confirmation on session close from Gremlin Server.
 * Use non-default port for running tests on Gremlin Server.
 * Fully shutdown metrics services in Gremlin Server on shutdown.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/52e3b86c/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
index 712110d4..2edfa17 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
@@ -22,10 +22,14 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.step.LambdaHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CyclicPathStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.SimplePathStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeOtherVertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.PathStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.TreeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeSideEffectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -63,31 +67,12 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
         implements TraversalStrategy.OptimizationStrategy {
 
     private static final IncidentToAdjacentStrategy INSTANCE = new IncidentToAdjacentStrategy();
-    private static final Set<Class> INVALIDATING_STEP_CLASSES = new HashSet<>(Arrays.asList(PathStep.class, LambdaHolder.class));
+    private static final Set<Class> INVALIDATING_STEP_CLASSES = new HashSet<>(Arrays.asList(CyclicPathStep.class,
+            PathStep.class, SimplePathStep.class, TreeStep.class, TreeSideEffectStep.class, LambdaHolder.class));
 
     private IncidentToAdjacentStrategy() {
     }
 
-    @Override
-    public void apply(Traversal.Admin<?, ?> traversal) {
-        final Traversal.Admin root = TraversalHelper.getRootTraversal(traversal);
-        if (TraversalHelper.hasStepOfAssignableClassRecursively(INVALIDATING_STEP_CLASSES, root))
-            return;
-        final Collection<Pair<VertexStep, Step>> stepsToReplace = new ArrayList<>();
-        Step prev = null;
-        for (final Step curr : traversal.getSteps()) {
-            if (isOptimizable(prev, curr)) {
-                stepsToReplace.add(Pair.with((VertexStep) prev, curr));
-            }
-            prev = curr;
-        }
-        if (!stepsToReplace.isEmpty()) {
-            for (final Pair<VertexStep, Step> pair : stepsToReplace) {
-                optimizeSteps(traversal, pair.getValue0(), pair.getValue1());
-            }
-        }
-    }
-
     /**
      * Checks whether a given step is optimizable or not.
      *
@@ -102,7 +87,8 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
             if (step1Dir.equals(Direction.BOTH)) {
                 return step2 instanceof EdgeOtherVertexStep;
             }
-            return step2 instanceof EdgeVertexStep && ((EdgeVertexStep) step2).getDirection().equals(step1Dir.opposite());
+            return step2 instanceof EdgeOtherVertexStep || (step2 instanceof EdgeVertexStep &&
+                    ((EdgeVertexStep) step2).getDirection().equals(step1Dir.opposite()));
         }
         return false;
     }
@@ -129,6 +115,26 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
     }
 
     @Override
+    public void apply(Traversal.Admin<?, ?> traversal) {
+        final Traversal.Admin root = TraversalHelper.getRootTraversal(traversal);
+        if (TraversalHelper.hasStepOfAssignableClassRecursively(INVALIDATING_STEP_CLASSES, root))
+            return;
+        final Collection<Pair<VertexStep, Step>> stepsToReplace = new ArrayList<>();
+        Step prev = null;
+        for (final Step curr : traversal.getSteps()) {
+            if (isOptimizable(prev, curr)) {
+                stepsToReplace.add(Pair.with((VertexStep) prev, curr));
+            }
+            prev = curr;
+        }
+        if (!stepsToReplace.isEmpty()) {
+            for (final Pair<VertexStep, Step> pair : stepsToReplace) {
+                optimizeSteps(traversal, pair.getValue0(), pair.getValue1());
+            }
+        }
+    }
+
+    @Override
     public Set<Class<? extends OptimizationStrategy>> applyPrior() {
         return Collections.singleton(IdentityRemovalStrategy.class);
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/52e3b86c/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
index 45f583e..4fa79b6 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
@@ -47,17 +47,16 @@ public class IncidentToAdjacentStrategyTest {
     @RunWith(Parameterized.class)
     public static class StandardTest extends AbstractIncidentToAdjacentStrategyTest {
 
-        @Parameterized.Parameters(name = "{0}")
-        public static Iterable<Object[]> data() {
-            return generateTestParameters();
-        }
-
         @Parameterized.Parameter(value = 0)
         public Traversal original;
-
         @Parameterized.Parameter(value = 1)
         public Traversal optimized;
 
+        @Parameterized.Parameters(name = "{0}")
+        public static Iterable<Object[]> data() {
+            return generateTestParameters();
+        }
+
         @Before
         public void setup() {
             this.traversalEngine = mock(TraversalEngine.class);
@@ -73,17 +72,16 @@ public class IncidentToAdjacentStrategyTest {
     @RunWith(Parameterized.class)
     public static class ComputerTest extends AbstractIncidentToAdjacentStrategyTest {
 
-        @Parameterized.Parameters(name = "{0}")
-        public static Iterable<Object[]> data() {
-            return generateTestParameters();
-        }
-
         @Parameterized.Parameter(value = 0)
         public Traversal original;
-
         @Parameterized.Parameter(value = 1)
         public Traversal optimized;
 
+        @Parameterized.Parameters(name = "{0}")
+        public static Iterable<Object[]> data() {
+            return generateTestParameters();
+        }
+
         @Before
         public void setup() {
             this.traversalEngine = mock(TraversalEngine.class);
@@ -100,21 +98,6 @@ public class IncidentToAdjacentStrategyTest {
 
         protected TraversalEngine traversalEngine;
 
-        void applyIncidentToAdjacentStrategy(final Traversal traversal) {
-            final TraversalStrategies strategies = new DefaultTraversalStrategies();
-            strategies.addStrategies(IncidentToAdjacentStrategy.instance());
-
-            traversal.asAdmin().setStrategies(strategies);
-            traversal.asAdmin().setEngine(this.traversalEngine);
-            traversal.asAdmin().applyStrategies();
-
-        }
-
-        public void doTest(final Traversal traversal, final Traversal optimized) {
-            applyIncidentToAdjacentStrategy(traversal);
-            assertEquals(optimized, traversal);
-        }
-
         static Iterable<Object[]> generateTestParameters() {
 
             Function<Traverser<Vertex>, Vertex> lambda = Traverser::get; // to ensure same hashCode
@@ -127,11 +110,30 @@ public class IncidentToAdjacentStrategyTest {
                     {__.bothE().bothV(), __.bothE().bothV()},
                     {__.bothE().inV(), __.bothE().inV()},
                     {__.bothE().outV(), __.bothE().outV()},
+                    {__.outE().otherV(), __.out()},
+                    {__.inE().otherV(), __.in()},
                     {__.outE().as("a").inV(), __.outE().as("a").inV()}, // todo: this can be optimized, but requires a lot more checks
                     {__.outE().inV().path(), __.outE().inV().path()},
+                    {__.outE().inV().simplePath(), __.outE().inV().simplePath()},
+                    {__.outE().inV().tree(), __.outE().inV().tree()},
                     {__.outE().inV().map(lambda), __.outE().inV().map(lambda)},
                     {__.union(__.outE().inV(), __.inE().outV()).path(), __.union(__.outE().inV(), __.inE().outV()).path()},
                     {__.as("a").outE().inV().as("b"), __.as("a").out().as("b")}});
         }
+
+        void applyIncidentToAdjacentStrategy(final Traversal traversal) {
+            final TraversalStrategies strategies = new DefaultTraversalStrategies();
+            strategies.addStrategies(IncidentToAdjacentStrategy.instance());
+
+            traversal.asAdmin().setStrategies(strategies);
+            traversal.asAdmin().setEngine(this.traversalEngine);
+            traversal.asAdmin().applyStrategies();
+
+        }
+
+        public void doTest(final Traversal traversal, final Traversal optimized) {
+            applyIncidentToAdjacentStrategy(traversal);
+            assertEquals(optimized, traversal);
+        }
     }
 }


[02/24] tinkerpop git commit: Added more content to traversal induced value recipe

Posted by sp...@apache.org.
Added more content to traversal induced value recipe


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

Branch: refs/heads/TINKERPOP-1600
Commit: e1d5b686172aa4caed3429adf04802fb3ef62a07
Parents: 6dba5ec
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jan 6 11:48:40 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jan 6 11:48:40 2017 -0500

----------------------------------------------------------------------
 .../recipes/traversal-induced-values.asciidoc   |  49 +++++++++++++++++++
 docs/static/images/eulerian-circuit.png         | Bin 0 -> 25270 bytes
 .../images/traversal-induced-values-1.png       | Bin 0 -> 12607 bytes
 .../images/traversal-induced-values-2.png       | Bin 0 -> 40440 bytes
 .../images/traversal-induced-values-3.png       | Bin 0 -> 49652 bytes
 5 files changed, 49 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1d5b686/docs/src/recipes/traversal-induced-values.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/traversal-induced-values.asciidoc b/docs/src/recipes/traversal-induced-values.asciidoc
index e292e16..03affbe 100644
--- a/docs/src/recipes/traversal-induced-values.asciidoc
+++ b/docs/src/recipes/traversal-induced-values.asciidoc
@@ -56,6 +56,55 @@ g.V().has('name','marko').as('marko').      <1>
 <3> Continue to traverser only if Marko's current friend is older than him.
 <4> Get the name of Marko's older friend.
 
+As another example of how traversal induced values can be used, consider a scenario where there was a graph that
+contained people, their friendship relationships, and the movies that they liked.
+
+image:traversal-induced-values-3.png[width=600]
+
+[gremlin-groovy]
+----
+g.addV("name", "alice", label, "user").as("u1").
+  addV("name", "jen", label, "user").as("u2").
+  addV("name", "dave", label, "user").as("u3").
+  addV("name", "the wild bunch", label, "movie").as("m1").
+  addV("name", "young guns", label, "movie").as("m2").
+  addV("name", "unforgiven", label, "movie").as("m3").
+  addE("friend").from("u1").to("u2").
+  addE("friend").from("u1").to("u3").
+  addE("like").from("u2").to("m1").
+  addE("like").from("u2").to("m2").
+  addE("like").from("u3").to("m2").
+  addE("like").from("u3").to("m3").iterate()
+----
+
+Getting a list of all the movies that Alice's friends like could be done like this:
+
+[gremlin-groovy,existing]
+----
+g.V().has('name','alice').out("friend").out("like").values("name")
+----
+
+but what if there was a need to get a list of movies that *all* her Alice's friends liked. In this case, that would
+mean filtering out "the wild bunch" and "unforgiven".
+
+[gremlin-groovy,existing]
+----
+g.V().has("name","alice").
+  out("friend").aggregate("friends").                            <1>
+  out("like").dedup().                                           <2>
+  filter(__.in("like").where(within("friends")).count().as("a"). <3>
+            select("friends").count(local).where(eq("a"))).      <4>
+  values("name")
+----
+
+<1> Gather Alice's list of friends to a list called "friends".
+<2> Traverse to the unique list of movies that Alice's friends like.
+<3> Remove movies that weren't liked by all friends. This starts by taking each movie and traversing back in on the
+"like" edges to friends who liked the movie (note the use of `where(within("friends"))` to limit those likes to only
+Alice's friends as aggregated in step one) and count them up into "a".
+<4> Count the aggregated friends and see if the number matches what was stored in "a" which would mean that all friends
+like the movie.
+
 Traversal induced values are not just for filtering. They can also be used when writing the values of the properties
 of one `Vertex` to another:
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1d5b686/docs/static/images/eulerian-circuit.png
----------------------------------------------------------------------
diff --git a/docs/static/images/eulerian-circuit.png b/docs/static/images/eulerian-circuit.png
new file mode 100755
index 0000000..d75674f
Binary files /dev/null and b/docs/static/images/eulerian-circuit.png differ

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1d5b686/docs/static/images/traversal-induced-values-1.png
----------------------------------------------------------------------
diff --git a/docs/static/images/traversal-induced-values-1.png b/docs/static/images/traversal-induced-values-1.png
new file mode 100755
index 0000000..3dc1eea
Binary files /dev/null and b/docs/static/images/traversal-induced-values-1.png differ

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1d5b686/docs/static/images/traversal-induced-values-2.png
----------------------------------------------------------------------
diff --git a/docs/static/images/traversal-induced-values-2.png b/docs/static/images/traversal-induced-values-2.png
new file mode 100755
index 0000000..cd17c55
Binary files /dev/null and b/docs/static/images/traversal-induced-values-2.png differ

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1d5b686/docs/static/images/traversal-induced-values-3.png
----------------------------------------------------------------------
diff --git a/docs/static/images/traversal-induced-values-3.png b/docs/static/images/traversal-induced-values-3.png
new file mode 100755
index 0000000..8e8c4b5
Binary files /dev/null and b/docs/static/images/traversal-induced-values-3.png differ


[06/24] tinkerpop git commit: Add an appendix to the recipes.

Posted by sp...@apache.org.
Add an appendix to the recipes.

Useful for general example traversals that don't fit nicely into a specific recipe or under a specific step in the reference documentation.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 043d1a3aa116b23a46cd1d77bed5ba3c0e4b77a2
Parents: d5404b8
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Jan 9 15:19:34 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Jan 9 15:19:34 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/index.asciidoc | 81 +++++++++++++++++++++++++++++++++++-
 1 file changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/043d1a3a/docs/src/recipes/index.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/index.asciidoc b/docs/src/recipes/index.asciidoc
index 8ffc358..2e88ac5 100644
--- a/docs/src/recipes/index.asciidoc
+++ b/docs/src/recipes/index.asciidoc
@@ -133,4 +133,83 @@ GitHub and JIRA are linked.  As mentioned earlier in this section, the recipe wi
 committers prior to merging. This process may take several days to complete. We look forward to receiving your
 submissions!
 
-
+Appendix
+========
+
+Many of the recipes are based on questions and answers provided on the gremlin-users mailing list. This section
+contains a number of traversals from the mailing list that do not easily fit any particular pattern (i.e. a recipe),
+but are nonetheless interesting and thus remain good tools for learning Gremlin.
+
+[[appendix-a]]
+_For each person in a "follows" graph, determine the number of followers and list their names._
+
+[gremlin-groovy]
+----
+g.addV('name','marko').as('marko').
+  addV('name','josh').as('josh').
+  addV('name','daniel').as('daniel').
+  addV('name','matthias').as('matthias').
+  addE('follows').from('josh').to('marko').
+  addE('follows').from('matthias').to('josh').
+  addE('follows').from('daniel').to('josh').
+  addE('follows').from('daniel').to('marko').iterate()
+g.V().as('p').
+  map(__.in('follows').values('name').fold()).
+  group().by(select('p').by('name')).
+          by(project('numFollowers','followers').
+               by(count(local)).by()).next()
+----
+
+[[appendix-b]]
+_In the "modern" graph, show each person, the software they worked on and the co-worker count for the software and
+the names of those co-workers._
+
+[gremlin-groovy,modern]
+----
+g.V().hasLabel("person").as("p").
+  out("created").as("s").
+  map(__.in("created").
+    where(neq("p")).values("name").fold()).
+  group().by(select("p").by("name")).
+    by(group().by(select("s").by("name")).
+    by(project("numCoworkers","coworkers").
+         by(count(local)).by())).next()
+----
+
+[[appendix-c]]
+_Assuming a graph of students, classes and times, detect students who have a conflicting schedule._
+
+[gremlin-groovy]
+----
+g.addV(label, "student", "name", "Pete").as("s1").
+  addV(label, "student", "name", "Joe").as("s2").
+  addV(label, "class", "name", "Java's GC").as("c1").
+  addV(label, "class", "name", "FP Principles").as("c2").
+  addV(label, "class", "name", "Memory Management in C").as("c3").
+  addV(label, "class", "name", "Memory Management in C++").as("c4").
+  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "10:00", "toTime", "11:00").as("t1").
+  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "11:00", "toTime", "12:00").as("t2").
+  addE("attends").from("s1").to("c1").
+  addE("attends").from("s1").to("c2").
+  addE("attends").from("s1").to("c3").
+  addE("attends").from("s1").to("c4").
+  addE("attends").from("s2").to("c2").
+  addE("attends").from("s2").to("c3").
+  addE("allocated").from("c1").to("t1").
+  addE("allocated").from("c1").to("t2").
+  addE("allocated").from("c2").to("t1").
+  addE("allocated").from("c3").to("t2").
+  addE("allocated").from("c4").to("t2").iterate()
+g.V().hasLabel("student").as("s").
+  out("attends").as("c").
+  out("allocated").as("t").
+  select("s").
+  out("attends").
+  where(neq("c")).
+  out("allocated").
+  where(eq("t")).
+  group().
+    by(select("s").by("name")).
+    by(group().by(select("t").by(valueMap("fromTime","toTime"))).
+               by(select("c").dedup().values("name").fold())).next()
+----
\ No newline at end of file


[15/24] tinkerpop git commit: Merge branch 'TINKERPOP-1545-tp31' into tp31

Posted by sp...@apache.org.
Merge branch 'TINKERPOP-1545-tp31' into tp31


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

Branch: refs/heads/TINKERPOP-1600
Commit: 4b8ea2703c1fe30b0345624b329d79d5ea1d6924
Parents: a27a048 52e3b86
Author: Daniel Kuppitz <da...@hotmail.com>
Authored: Thu Jan 12 15:22:33 2017 +0100
Committer: Daniel Kuppitz <da...@hotmail.com>
Committed: Thu Jan 12 15:22:33 2017 +0100

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../IncidentToAdjacentStrategy.java             | 50 +++++++++--------
 .../IncidentToAdjacentStrategyTest.java         | 56 ++++++++++----------
 3 files changed, 58 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[11/24] tinkerpop git commit: Added recipe to appendix for some basic math ops

Posted by sp...@apache.org.
Added recipe to appendix for some basic math ops


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

Branch: refs/heads/TINKERPOP-1600
Commit: 6971e644ffc0306a968c43f055a7849761108f60
Parents: 0938ebd
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 11:06:48 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 11:06:48 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 12 ++++++++++++
 1 file changed, 12 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6971e644/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 15d7340..369d049 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -125,3 +125,15 @@ g.V().not(has("location", without(places))).
   where(values("location").is(within(places)).count().is(places.size())).
   valueMap()
 ----
+
+[[appendix-f]]
+_Methods for performing some basic mathematical operations in the "modern" graph._
+
+[gremlin-groovy,modern]
+----
+g.V().values("age").sum() // sum all ages
+g.V().values("age").fold(1, mult) // multiply all ages
+g.V().values("age").map(union(identity(), constant(-1)).sum()) // subtract 1
+g.V().values("age").map(union(identity(), identity()).sum()) // multiply by 2 (simple)
+g.V().values("age").map(union(identity(), constant(2)).fold(1, mult)) // multiply by 2 (generally useful for multiplications by n):
+----
\ No newline at end of file


[24/24] tinkerpop git commit: TINKERPOP-1600 Added base64 encoded string to sasl challenge

Posted by sp...@apache.org.
TINKERPOP-1600 Added base64 encoded string to sasl challenge


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

Branch: refs/heads/TINKERPOP-1600
Commit: 866e96efac994976e2c222f817b38c3d1abadc5b
Parents: 802ff9f
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Jan 16 15:22:07 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Jan 18 06:35:51 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                                  |  2 +-
 docs/src/dev/io/graphson.asciidoc                   |  5 ++---
 docs/src/upgrade/release-3.2.x-incubating.asciidoc  | 16 ++++++++++++++++
 .../apache/tinkerpop/gremlin/driver/Handler.java    | 15 +++++++++++++--
 .../server/handler/SaslAuthenticationHandler.java   | 14 ++++++++++++--
 5 files changed, 44 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/866e96ef/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9471b9f..4f3f9ce 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)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* SASL negotiation supports both a byte array and Base64 encoded bytes as a string for authentication to Gremlin Server.
 * Deprecated `TinkerIoRegistry` replacing it with the more consistently named `TinkerIoRegistryV1d0`.
 * Made error messaging more consistent during result iteration timeouts in Gremlin Server.
 * `PathRetractionStrategy` does not add a `NoOpBarrierStep` to the end of local children as its wasted computation in 99% of traversals.
@@ -580,7 +581,6 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.1.6 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* Bumped to Groovy 2.4.8.
 * Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating steps.
 * Returned a confirmation on session close from Gremlin Server.
 * Use non-default port for running tests on Gremlin Server.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/866e96ef/docs/src/dev/io/graphson.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/dev/io/graphson.asciidoc b/docs/src/dev/io/graphson.asciidoc
index cdc9880..fe56c27 100644
--- a/docs/src/dev/io/graphson.asciidoc
+++ b/docs/src/dev/io/graphson.asciidoc
@@ -1299,7 +1299,7 @@ ResponseMessage
 Authentication Challenge
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-When authentication is enabled, an initial request to the server will result in an authentication challenge. The typical response message will appear as follows, but handling it could be different dependending on the SASL implementation (e.g. multiple challenges maybe requested in some cases, but no in the default provided by Gremlin Server).
+When authentication is enabled, an initial request to the server will result in an authentication challenge. The typical response message will appear as follows, but handling it could be different dependending on the SASL implementation (e.g. multiple challenges maybe requested in some cases, but not in the default provided by Gremlin Server).
 
 [source,json]
 ----
@@ -4262,14 +4262,13 @@ The following `RequestMessage` is an example of a sessionless request for a scri
 }
 ----
 
-
 ResponseMessage
 ~~~~~~~~~~~~~~~
 
 Authentication Challenge
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-When authentication is enabled, an initial request to the server will result in an authentication challenge. The typical response message will appear as follows, but handling it could be different dependending on the SASL implementation (e.g. multiple challenges maybe requested in some cases, but no in the default provided by Gremlin Server).
+When authentication is enabled, an initial request to the server will result in an authentication challenge. The typical response message will appear as follows, but handling it could be different dependending on the SASL implementation (e.g. multiple challenges maybe requested in some cases, but not in the default provided by Gremlin Server).
 
 [source,json]
 ----

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/866e96ef/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 8d73baed..b478b96 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -240,6 +240,22 @@ and defaults to `false`.
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-932[TINKERPOP-932],
 link:http://tinkerpop.apache.org/docs/current/dev/provider/#_session_opprocessor[Provider Documentation - Session OpProcessor]
 
+SASL Authentication
++++++++++++++++++++
+
+Gremlin Supports SASL based authentication. The server accepts either a byte array or Base64 encoded String as the in
+the `sasl` argument on the `RequestMessage`, however it sends back a byte array only. Some serializers or serializer
+configurations don't work well with that approach (specifically the "toString" configuration on the Gryo serializer) as
+the byte array is returned in the `ResponseMessage` result. In the case of the "toString" serializer the byte array
+gets "toString'd" and the can't be read by the client.
+
+In 3.2.4, the byte array is still returned in the `ResponseMessage` result, but is also returned in the status
+attributes under a `sasl` key as a Base64 encoded string. In this way, the client has options on how it chooses to
+process the authentication response and the change remains backward compatible. Drivers should upgrade to using the
+Base64 encoded string however as the old approach will likely be removed in the future.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1600[TINKERPOP-1600]
+
 TinkerPop 3.2.3
 ---------------
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/866e96ef/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
index 681354c..650bc2f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
@@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
 import java.net.InetSocketAddress;
 import java.security.PrivilegedExceptionAction;
 import java.security.PrivilegedActionException;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -70,6 +71,9 @@ final class Handler {
         private static final Map<String, String> SASL_PROPERTIES = new HashMap<String, String>() {{ put(Sasl.SERVER_AUTH, "true"); }};
         private static final byte[] NULL_CHALLENGE = new byte[0];
 
+        private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
+        private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
+
         private final AuthProperties authProps;
 
         public GremlinSaslAuthenticationHandler(final AuthProperties authProps) {
@@ -103,9 +107,16 @@ final class Handler {
 
                     messageBuilder.addArg(Tokens.ARGS_SASL_MECHANISM, getMechanism());
                     messageBuilder.addArg(Tokens.ARGS_SASL, saslClient.get().hasInitialResponse() ?
-                                                                evaluateChallenge(subject, saslClient, NULL_CHALLENGE) : null);
+                            BASE64_ENCODER.encodeToString(evaluateChallenge(subject, saslClient, NULL_CHALLENGE)) : null);
                 } else {
-                    messageBuilder.addArg(Tokens.ARGS_SASL, evaluateChallenge(subject, saslClient, (byte[])response.getResult().getData()));
+                    // the server sends base64 encoded sasl as well as the byte array. the byte array will eventually be
+                    // phased out, but is present now for backward compatibility in 3.2.x
+                    final String base64sasl = response.getStatus().getAttributes().containsKey(Tokens.ARGS_SASL) ?
+                        response.getStatus().getAttributes().get(Tokens.ARGS_SASL).toString() :
+                        BASE64_ENCODER.encodeToString((byte[]) response.getResult().getData());
+
+                    messageBuilder.addArg(Tokens.ARGS_SASL, BASE64_ENCODER.encodeToString(
+                        evaluateChallenge(subject, saslClient, BASE64_DECODER.decode(base64sasl))));
                 }
                 channelHandlerContext.writeAndFlush(messageBuilder.create());
             } else {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/866e96ef/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
index 4bb7879..2d7edf0 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
@@ -28,6 +28,9 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -50,6 +53,8 @@ import org.slf4j.LoggerFactory;
 @ChannelHandler.Sharable
 public class SaslAuthenticationHandler extends ChannelInboundHandlerAdapter {
     private static final Logger logger = LoggerFactory.getLogger(SaslAuthenticationHandler.class);
+    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
+    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
 
     private final Authenticator authenticator;
 
@@ -80,7 +85,7 @@ public class SaslAuthenticationHandler extends ChannelInboundHandlerAdapter {
                     if (saslObject instanceof byte[]) {
                         saslResponse = (byte[]) saslObject;
                     } else if(saslObject instanceof String) {
-                        saslResponse = Base64.getDecoder().decode((String) saslObject);
+                        saslResponse = BASE64_DECODER.decode((String) saslObject);
                     } else {
                         final ResponseMessage error = ResponseMessage.build(request.get())
                                 .statusMessage("Incorrect type for : " + Tokens.ARGS_SASL + " - byte[] or base64 encoded String is expected")
@@ -101,8 +106,13 @@ public class SaslAuthenticationHandler extends ChannelInboundHandlerAdapter {
                             final RequestMessage original = request.get();
                             ctx.fireChannelRead(original);
                         } else {
-                            // not done here - send back the sasl message for next challenge
+                            // not done here - send back the sasl message for next challenge. note that we send back
+                            // the base64 encoded sasl as well as the byte array. the byte array will eventually be
+                            // phased out, but is present now for backward compatibility in 3.2.x
+                            final Map<String,Object> metadata = new HashMap<>();
+                            metadata.put(Tokens.ARGS_SASL, BASE64_ENCODER.encode(saslMessage));
                             final ResponseMessage authenticate = ResponseMessage.build(requestMessage)
+                                    .statusAttributes(metadata)
                                     .code(ResponseStatusCode.AUTHENTICATE).result(saslMessage).create();
                             ctx.writeAndFlush(authenticate);
                         }


[09/24] tinkerpop git commit: Added duplicate vertex recipe

Posted by sp...@apache.org.
Added duplicate vertex recipe


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

Branch: refs/heads/TINKERPOP-1600
Commit: d7ecfc05f9c476fed2ebf987e6b26bee14f5db54
Parents: 3e54d89
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 10:19:59 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 10:19:59 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/duplicate-vertex.asciidoc | 52 +++++++++++++++++++++++++
 docs/src/recipes/index.asciidoc            |  2 +
 2 files changed, 54 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d7ecfc05/docs/src/recipes/duplicate-vertex.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/duplicate-vertex.asciidoc b/docs/src/recipes/duplicate-vertex.asciidoc
new file mode 100644
index 0000000..e0327f4
--- /dev/null
+++ b/docs/src/recipes/duplicate-vertex.asciidoc
@@ -0,0 +1,52 @@
+////
+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.
+////
+[[duplicate-vertex]]
+Duplicate Vertex Detection
+--------------------------
+
+The pattern for finding duplicate vertices is quite similar to the pattern defined in the <<duplicate-edge,Duplicate Edge>>
+section. The idea is to extract the relevant features of the vertex into a comparable list that can then be used to
+group for duplicates.
+
+Consider the following example with some duplicate vertices added to the "modern" graph:
+
+[gremlin-groovy,modern]
+----
+g.addV(label, 'person', 'name', 'vadas', 'age', 27)
+g.addV(label, 'person', 'name', 'vadas', 'age', 22) // not a duplicate because "age" value
+g.addV(label, 'person', 'name', 'marko', 'age', 29)
+g.V().hasLabel("person").
+  group().
+    by(values("name", "age").fold()).
+  unfold()
+----
+
+In the above case, the "name" and "age" properties are the relevant features for identifying duplication. The key in
+the `Map` provided by the `group` is the list of features for comparison and the value is the list of vertices that
+match the feature. To extract just those vertices that contain duplicates an additional filter can be added:
+
+[gremlin-groovy,existing]
+----
+g.V().hasLabel("person").
+  group().
+    by(values("name", "age").fold()).
+  unfold().
+  filter(select(values).count(local).is(gt(1)))
+----
+
+That filter, extracts the values of the `Map` and counts the vertices within each list. If that list contains more than
+one vertex then it is a duplicate.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d7ecfc05/docs/src/recipes/index.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/index.asciidoc b/docs/src/recipes/index.asciidoc
index f77b929..31095c0 100644
--- a/docs/src/recipes/index.asciidoc
+++ b/docs/src/recipes/index.asciidoc
@@ -46,6 +46,8 @@ include::cycle-detection.asciidoc[]
 
 include::duplicate-edge.asciidoc[]
 
+include::duplicate-vertex.asciidoc[]
+
 include::if-then-based-grouping.asciidoc[]
 
 include::pagination.asciidoc[]


[03/24] tinkerpop git commit: Added an additional example to project() step

Posted by sp...@apache.org.
Added an additional example to project() step


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

Branch: refs/heads/TINKERPOP-1600
Commit: 52f7f965879bf881296a2368be9265d503b95ad9
Parents: e1d5b68
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jan 6 13:36:50 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jan 6 13:36:50 2017 -0500

----------------------------------------------------------------------
 docs/src/reference/the-traversal.asciidoc | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/52f7f965/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 52cebf8..6380efc 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1480,6 +1480,10 @@ g.V().out('created').
     by(__.in('created').count()).
   order().by(select('b'),decr).
   select('a')
+g.V().has('name','marko').
+               project('out','in').
+                 by(outE().count()).
+                 by(inE().count())
 ----
 
 [[program-step]]


[13/24] tinkerpop git commit: Improved the section description for the recipes appendix

Posted by sp...@apache.org.
Improved the section description for the recipes appendix


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

Branch: refs/heads/TINKERPOP-1600
Commit: 0fbfd75afbb5654b079111e928020250905327a7
Parents: 79a2468
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Jan 12 07:38:48 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jan 12 07:38:48 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0fbfd75a/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 6f9fb2c..9038cea 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -17,9 +17,11 @@ limitations under the License.
 Appendix
 ========
 
-Many of the recipes are based on questions and answers provided on the gremlin-users mailing list. This section
-contains a number of traversals from the mailing list that do not easily fit any particular pattern (i.e. a recipe),
-but are nonetheless interesting and thus remain good tools for learning Gremlin.
+Many of the recipes are based on questions and answers provided on the
+link:https://groups.google.com/forum/#!forum/gremlin-users[gremlin-users mailing list] or on
+link:http://stackoverflow.com/questions/tagged/gremlin[StackOverflow]. This section contains those traversals from
+those sources that do not easily fit any particular pattern (i.e. a recipe), but are nonetheless interesting and thus
+remain good tools for learning Gremlin.
 
 [[appendix-a]]
 _For each person in a "follows" graph, determine the number of followers and list their names._


[18/24] tinkerpop git commit: Fixed up an appendix entry given pull request feedback.

Posted by sp...@apache.org.
Fixed up an appendix entry given pull request feedback.


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

Branch: refs/heads/TINKERPOP-1600
Commit: f992cdfe89b00e91df98e83907c65968d16dcb17
Parents: 1c14dde
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 11:38:18 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 11:38:18 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/f992cdfe/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 15fda50..e9d13aa 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -38,9 +38,10 @@ g.addV('name','marko').as('marko').
   addE('follows').from('daniel').to('marko').iterate()
 g.V().as('p').
   map(__.in('follows').values('name').fold()).
-  group().by(select('p').by('name')).
-          by(project('numFollowers','followers').
-               by(count(local)).by()).next()
+  project('person','followers','numFollowers').
+    by(select('p').by('name')).
+    by().
+    by(count(local))
 ----
 
 It might also be alternatively written as:


[21/24] tinkerpop git commit: Bump to groovy 2.4.8

Posted by sp...@apache.org.
Bump to groovy 2.4.8

https://lists.apache.org/thread.html/50dd28c50a15c422b3e7277d0aa4c59a8831523d4ae7d5afe73e8573@%3Cdev.tinkerpop.apache.org%3E CTR


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

Branch: refs/heads/TINKERPOP-1600
Commit: 6e2128cbac8fb7bd7e42ba262cb0b1f559e4a385
Parents: 4b8ea27
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 13:01:49 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 13:01:49 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc | 1 +
 pom.xml            | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6e2128cb/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index f82fa23..d498599 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.1.6 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Bumped to Groovy 2.4.8.
 * Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating steps.
 * Returned a confirmation on session close from Gremlin Server.
 * Use non-default port for running tests on Gremlin Server.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6e2128cb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ee4b965..d8311ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,7 +127,7 @@ limitations under the License.
     </scm>
     <properties>
         <commons.configuration.version>1.10</commons.configuration.version>
-        <groovy.version>2.4.6</groovy.version>
+        <groovy.version>2.4.8</groovy.version>
         <junit.version>4.12</junit.version>
         <metrics.version>3.0.2</metrics.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>


[16/24] tinkerpop git commit: Add another example for appendix d of recipes.

Posted by sp...@apache.org.
Add another example for appendix d of recipes.

The addition find vertex pairs with more than one edge between them assuming undirected edges.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 1c14dde500f24f90eabb3d0517ecce6c8626100f
Parents: 4be8a1b
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Jan 16 15:36:34 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Jan 16 15:36:34 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 10 ++++++++++
 1 file changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1c14dde5/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 9d0ea7b..15fda50 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -136,6 +136,16 @@ g.V().as("a").
   select(keys)
 ----
 
+The following example assumes that the edges point in the `OUT` direction. Assuming undirected edges:
+
+[gremlin-groovy,modern]
+----
+g.V().where(without("x")).as("a").
+  outE().as("e").inV().as("b").
+  filter(bothE().where(neq("e")).otherV().where(eq("a"))).store("x").
+  select("a","b").dedup()
+----
+
 [[appendix-e]]
 _In the "crew" graph, find vertices that match on a complete set of multi-properties._
 


[22/24] tinkerpop git commit: Merge branch 'tp31' into tp32

Posted by sp...@apache.org.
Merge branch 'tp31' into tp32

Conflicts:
	CHANGELOG.asciidoc
	gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
	gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
	pom.xml


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

Branch: refs/heads/TINKERPOP-1600
Commit: 3bb177fef12727184575477d28ef163a956c01bf
Parents: e06ce0c 6e2128c
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 13:55:24 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 13:55:24 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc | 1 +
 pom.xml            | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3bb177fe/CHANGELOG.asciidoc
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3bb177fe/pom.xml
----------------------------------------------------------------------
diff --cc pom.xml
index 3c5f8ed,d8311ca..0845ef8
--- a/pom.xml
+++ b/pom.xml
@@@ -134,7 -127,8 +134,7 @@@ limitations under the License
      </scm>
      <properties>
          <commons.configuration.version>1.10</commons.configuration.version>
-         <groovy.version>2.4.7</groovy.version>
+         <groovy.version>2.4.8</groovy.version>
 -        <junit.version>4.12</junit.version>
          <metrics.version>3.0.2</metrics.version>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>


[12/24] tinkerpop git commit: Added some more appendix items to recipes

Posted by sp...@apache.org.
Added some more appendix items to recipes


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

Branch: refs/heads/TINKERPOP-1600
Commit: 79a246851493e4f5a39644a9e86d828b39db0b2c
Parents: 6971e64
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 12:13:59 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 12:13:59 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 87 +++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/79a24685/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 369d049..6f9fb2c 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -136,4 +136,91 @@ g.V().values("age").fold(1, mult) // multiply all ages
 g.V().values("age").map(union(identity(), constant(-1)).sum()) // subtract 1
 g.V().values("age").map(union(identity(), identity()).sum()) // multiply by 2 (simple)
 g.V().values("age").map(union(identity(), constant(2)).fold(1, mult)) // multiply by 2 (generally useful for multiplications by n):
+----
+
+[[appendix-g]]
+_Dropping a vertex, as well as the vertices related to that dropped vertex that are connected by a "knows" edge in the
+"modern" graph_
+
+[gremlin-groovy,modern]
+----
+g.V().has('name','marko').outE()
+g.V().has('name','marko').sideEffect(out('knows').drop()).drop()
+g.V().has('name','marko')
+g.V(2,4,3)
+----
+
+[[appendix-h]]
+_For the specified graph, find all neighbor vertices connected to "A" as filtered by datetime, those neighbor vertices
+that don't have datetime vertices, and those neighbor vertices that have the label "dimon"._
+
+[gremlin-groovy]
+----
+g.addV().property("name", "A").as("a").
+  addV().property("name", "B").as("b").
+  addV().property("name", "C").as("c").
+  addV().property("name", "D").as("d").
+  addV().property("name", "E").as("e").
+  addV("dimon").property("name", "F").as("f").
+  addV().property("name", "G").as("g").property("date", 20160818).
+  addV().property("name", "H").as("h").property("date", 20160817).
+  addE("rel").from("a").to("b").
+  addE("rel").from("a").to("c").
+  addE("rel").from("a").to("d").
+  addE("rel").from("a").to("e").
+  addE("rel").from("c").to("f").
+  addE("occured_at").from("d").to("g").
+  addE("occured_at").from("e").to("h").iterate()
+// D and E have a valid datetime
+g.V().has("name", "A").out("rel").
+  union(where(out("occured_at").has("date", gte(20160817))),
+        __.not(outE("occured_at")).coalesce(out().hasLabel("dimon"), identity())).
+  valueMap()
+// only E has a valid date
+g.V().has("name", "A").out("rel").
+  union(where(out("occured_at").has("date", lte(20160817))),
+        __.not(outE("occured_at")).coalesce(out().hasLabel("dimon"), identity())).
+  valueMap()
+// only D has a valid date
+g.V().has("name", "A").out("rel").
+  union(where(out("occured_at").has("date", gt(20160817))),
+        __.not(outE("occured_at")).coalesce(out().hasLabel("dimon"), identity())).
+  valueMap()
+// neither D nor E have a valid date
+g.V().has("name", "A").out("rel").
+  union(where(out("occured_at").has("date", lt(20160817))),
+        __.not(outE("occured_at")).coalesce(out().hasLabel("dimon"), identity())).
+  valueMap()
+----
+
+[[appendix-i]]
+_Use element labels in a `select`._
+
+[gremlin-groovy,modern]
+----
+g.V(1).as("a").
+  both().
+  map(group().by(label).by(unfold())).as("b").
+  select("a","b").
+  map(union(project("a").by(select("a")), select("b")).
+  unfold().
+  group().
+    by(select(keys)).
+    by(select(values)))
+g.V().as("a").
+  both().
+  map(group().by(label).by(unfold())).as("b").
+  select("a","b").
+  group().
+    by(select("a")).
+    by(select("b").
+         group().
+           by(select(keys)).
+           by(select(values).fold())).
+    unfold().
+    map(union(select(keys).project("a").by(), select(values)).
+    unfold().
+    group().
+      by(select(keys).unfold()).
+      by(select(values).unfold().unfold().fold()))
 ----
\ No newline at end of file


[23/24] tinkerpop git commit: Consistent naming for TinkerIoRegistry

Posted by sp...@apache.org.
Consistent naming for TinkerIoRegistry

Now that there are multiple versions of GraphSON/Gryo the naming of registries should reflect the version number. This is a non-breaking change. The old TinkerIoRegistry is just deprecated. CTR


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

Branch: refs/heads/TINKERPOP-1600
Commit: 802ff9f69b48b4aa56a85c586ed3c58844a89c9f
Parents: 3bb177f
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 19:04:03 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 19:04:03 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 gremlin-console/conf/remote-objects.yaml        |   2 +-
 .../tinkergraph/structure/TinkerGraph.java      |   2 +-
 .../tinkergraph/structure/TinkerIoRegistry.java |   2 +
 .../structure/TinkerIoRegistryV1d0.java         | 263 +++++++++++++++++++
 .../structure/TinkerIoRegistryV2d0.java         |   5 +-
 .../TinkerGraphGryoSerializerTest.java          |   1 -
 .../TinkerGraphGryoSerializerV1d0Test.java      |  83 ++++++
 .../TinkerGraphGryoSerializerV2d0Test.java      |  80 ++++++
 .../tinkergraph/structure/TinkerGraphTest.java  |  30 ++-
 10 files changed, 463 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 42088dd..9471b9f 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)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Deprecated `TinkerIoRegistry` replacing it with the more consistently named `TinkerIoRegistryV1d0`.
 * Made error messaging more consistent during result iteration timeouts in Gremlin Server.
 * `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.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/gremlin-console/conf/remote-objects.yaml
----------------------------------------------------------------------
diff --git a/gremlin-console/conf/remote-objects.yaml b/gremlin-console/conf/remote-objects.yaml
index f507f4e..d1909fc 100644
--- a/gremlin-console/conf/remote-objects.yaml
+++ b/gremlin-console/conf/remote-objects.yaml
@@ -38,4 +38,4 @@
 hosts: [localhost]
 port: 8182
 serializer: { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0,
-              config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistry] }}
\ No newline at end of file
+              config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0] }}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index 48f4d3d..fa8c62b 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -233,7 +233,7 @@ public final class TinkerGraph implements Graph {
 
     @Override
     public <I extends Io> I io(final Io.Builder<I> builder) {
-        return (I) builder.graph(this).onMapper(mapper -> mapper.addRegistry(TinkerIoRegistry.instance())).create();
+        return (I) builder.graph(this).onMapper(mapper -> mapper.addRegistry(TinkerIoRegistryV1d0.instance())).create();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistry.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistry.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistry.java
index 66bdba1..40720e5 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistry.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistry.java
@@ -70,7 +70,9 @@ import java.util.Map;
  * custom data class like a "geographic point".
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @deprecated As of release 3.2.4, replaced by {@link TinkerIoRegistryV1d0}.
  */
+@Deprecated
 public final class TinkerIoRegistry extends AbstractIoRegistry {
 
     private static final TinkerIoRegistry INSTANCE = new TinkerIoRegistry();

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV1d0.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV1d0.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV1d0.java
new file mode 100644
index 0000000..277bedc
--- /dev/null
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV1d0.java
@@ -0,0 +1,263 @@
+/*
+ * 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.tinkergraph.structure;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.AbstractIoRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.GraphReader;
+import org.apache.tinkerpop.gremlin.structure.io.GraphWriter;
+import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil;
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoIo;
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoReader;
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter;
+import org.apache.tinkerpop.gremlin.structure.util.Attachable;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
+import org.apache.tinkerpop.shaded.jackson.core.JsonParser;
+import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException;
+import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext;
+import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider;
+import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer;
+import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule;
+import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer;
+import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Serializer;
+import org.apache.tinkerpop.shaded.kryo.io.Input;
+import org.apache.tinkerpop.shaded.kryo.io.Output;
+import org.javatuples.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An implementation of the {@link IoRegistry} interface that provides serializers with custom configurations for
+ * implementation specific classes that might need to be serialized.  This registry allows a {@link TinkerGraph} to
+ * be serialized directly which is useful for moving small graphs around on the network.
+ * <p/>
+ * Most providers need not implement this kind of custom serializer as they will deal with much larger graphs that
+ * wouldn't be practical to serialize in this fashion.  This is a bit of a special case for TinkerGraph given its
+ * in-memory status.  Typical implementations would create serializers for a complex vertex identifier or a
+ * custom data class like a "geographic point".
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class TinkerIoRegistryV1d0 extends AbstractIoRegistry {
+
+    private static final TinkerIoRegistryV1d0 INSTANCE = new TinkerIoRegistryV1d0();
+
+    private TinkerIoRegistryV1d0() {
+        register(GryoIo.class, TinkerGraph.class, new TinkerGraphGryoSerializer());
+        register(GraphSONIo.class, null, new TinkerModule());
+    }
+
+    public static TinkerIoRegistryV1d0 instance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Provides a method to serialize an entire {@link TinkerGraph} into itself for Gryo.  This is useful when
+     * shipping small graphs around through Gremlin Server. Reuses the existing Kryo instance for serialization.
+     */
+    final static class TinkerGraphGryoSerializer extends Serializer<TinkerGraph> {
+        @Override
+        public void write(final Kryo kryo, final Output output, final TinkerGraph graph) {
+            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+                GryoWriter.build().mapper(() -> kryo).create().writeGraph(stream, graph);
+                final byte[] bytes = stream.toByteArray();
+                output.writeInt(bytes.length);
+                output.write(bytes);
+            } catch (Exception io) {
+                throw new RuntimeException(io);
+            }
+        }
+
+        @Override
+        public TinkerGraph read(final Kryo kryo, final Input input, final Class<TinkerGraph> tinkerGraphClass) {
+            final Configuration conf = new BaseConfiguration();
+            conf.setProperty("gremlin.tinkergraph.defaultVertexPropertyCardinality", "list");
+            final TinkerGraph graph = TinkerGraph.open(conf);
+            final int len = input.readInt();
+            final byte[] bytes = input.readBytes(len);
+            try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
+                GryoReader.build().mapper(() -> kryo).create().readGraph(stream, graph);
+            } catch (Exception io) {
+                throw new RuntimeException(io);
+            }
+
+            return graph;
+        }
+    }
+
+    /**
+     * Provides a method to serialize an entire {@link TinkerGraph} into itself for GraphSON.  This is useful when
+     * shipping small graphs around through Gremlin Server.
+     */
+    final static class TinkerModule extends SimpleModule {
+        public TinkerModule() {
+            super("tinkergraph-1.0");
+            addSerializer(TinkerGraph.class, new TinkerGraphJacksonSerializer());
+            addDeserializer(TinkerGraph.class, new TinkerGraphJacksonDeserializer());
+        }
+    }
+
+    /**
+     * Serializes the graph into an edge list format.  Edge list is a better choices than adjacency list (which is
+     * typically standard from the {@link GraphReader} and {@link GraphWriter} perspective) in this case because
+     * the use case for this isn't around massive graphs.  The use case is for "small" subgraphs that are being
+     * shipped over the wire from Gremlin Server. Edge list format is a bit easier for non-JVM languages to work
+     * with as a format and doesn't require a cache for loading (as vertex labels are not serialized in adjacency
+     * list).
+     */
+    final static class TinkerGraphJacksonSerializer extends StdSerializer<TinkerGraph> {
+
+        public TinkerGraphJacksonSerializer() {
+            super(TinkerGraph.class);
+        }
+
+        @Override
+        public void serialize(final TinkerGraph graph, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+                throws IOException {
+            jsonGenerator.writeStartObject();
+
+            jsonGenerator.writeFieldName(GraphSONTokens.VERTICES);
+            jsonGenerator.writeStartArray();
+
+            final Iterator<Vertex> vertices = graph.vertices();
+            while (vertices.hasNext()) {
+                serializerProvider.defaultSerializeValue(vertices.next(), jsonGenerator);
+            }
+
+            jsonGenerator.writeEndArray();
+
+            jsonGenerator.writeFieldName(GraphSONTokens.EDGES);
+            jsonGenerator.writeStartArray();
+
+            final Iterator<Edge> edges = graph.edges();
+            while (edges.hasNext()) {
+                serializerProvider.defaultSerializeValue(edges.next(), jsonGenerator);
+            }
+
+            jsonGenerator.writeEndArray();
+
+            jsonGenerator.writeEndObject();
+        }
+
+        @Override
+        public void serializeWithType(final TinkerGraph graph, final JsonGenerator jsonGenerator,
+                                      final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+            jsonGenerator.writeStartObject();
+            jsonGenerator.writeStringField(GraphSONTokens.CLASS, TinkerGraph.class.getName());
+
+            jsonGenerator.writeFieldName(GraphSONTokens.VERTICES);
+            jsonGenerator.writeStartArray();
+            jsonGenerator.writeString(ArrayList.class.getName());
+            jsonGenerator.writeStartArray();
+
+            final Iterator<Vertex> vertices = graph.vertices();
+            while (vertices.hasNext()) {
+                GraphSONUtil.writeWithType(vertices.next(), jsonGenerator, serializerProvider, typeSerializer);
+            }
+
+            jsonGenerator.writeEndArray();
+            jsonGenerator.writeEndArray();
+
+            jsonGenerator.writeFieldName(GraphSONTokens.EDGES);
+            jsonGenerator.writeStartArray();
+            jsonGenerator.writeString(ArrayList.class.getName());
+            jsonGenerator.writeStartArray();
+
+            final Iterator<Edge> edges = graph.edges();
+            while (edges.hasNext()) {
+                GraphSONUtil.writeWithType(edges.next(), jsonGenerator, serializerProvider, typeSerializer);
+            }
+
+            jsonGenerator.writeEndArray();
+            jsonGenerator.writeEndArray();
+
+            jsonGenerator.writeEndObject();
+        }
+    }
+
+    /**
+     * Deserializes the edge list format.
+     */
+    static class TinkerGraphJacksonDeserializer extends StdDeserializer<TinkerGraph> {
+        public TinkerGraphJacksonDeserializer() {
+            super(TinkerGraph.class);
+        }
+
+        @Override
+        public TinkerGraph deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+            final Configuration conf = new BaseConfiguration();
+            conf.setProperty("gremlin.tinkergraph.defaultVertexPropertyCardinality", "list");
+            final TinkerGraph graph = TinkerGraph.open(conf);
+
+            final List<Map<String, Object>> edges;
+            final List<Map<String, Object>> vertices;
+            if (!jsonParser.getCurrentToken().isStructStart()) {
+                if (!jsonParser.getCurrentName().equals(GraphSONTokens.VERTICES))
+                    throw new IOException(String.format("Expected a '%s' key", GraphSONTokens.VERTICES));
+
+                jsonParser.nextToken();
+                vertices = (List<Map<String, Object>>) deserializationContext.readValue(jsonParser, ArrayList.class);
+                jsonParser.nextToken();
+
+                if (!jsonParser.getCurrentName().equals(GraphSONTokens.EDGES))
+                    throw new IOException(String.format("Expected a '%s' key", GraphSONTokens.EDGES));
+
+                jsonParser.nextToken();
+                edges = (List<Map<String, Object>>) deserializationContext.readValue(jsonParser, ArrayList.class);
+            } else {
+                final Map<String, Object> graphData = deserializationContext.readValue(jsonParser, HashMap.class);
+                vertices = (List<Map<String,Object>>) graphData.get(GraphSONTokens.VERTICES);
+                edges = (List<Map<String,Object>>) graphData.get(GraphSONTokens.EDGES);
+            }
+
+            for (Map<String, Object> vertexData : vertices) {
+                final DetachedVertex detached = new DetachedVertex(vertexData.get(GraphSONTokens.ID),
+                        vertexData.get(GraphSONTokens.LABEL).toString(), (Map<String,Object>) vertexData.get(GraphSONTokens.PROPERTIES));
+                detached.attach(Attachable.Method.getOrCreate(graph));
+            }
+
+            for (Map<String, Object> edgeData : edges) {
+                final DetachedEdge detached = new DetachedEdge(edgeData.get(GraphSONTokens.ID),
+                        edgeData.get(GraphSONTokens.LABEL).toString(), (Map<String,Object>) edgeData.get(GraphSONTokens.PROPERTIES),
+                        Pair.with(edgeData.get(GraphSONTokens.OUT), edgeData.get(GraphSONTokens.OUT_LABEL).toString()),
+                        Pair.with(edgeData.get(GraphSONTokens.IN), edgeData.get(GraphSONTokens.IN_LABEL).toString()));
+                detached.attach(Attachable.Method.getOrCreate(graph));
+            }
+
+            return graph;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java
index 32446a5..727931c 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java
@@ -23,6 +23,7 @@ import org.apache.commons.configuration.Configuration;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.io.AbstractIoRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil;
@@ -56,8 +57,8 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * An implementation of the {@link org.apache.tinkerpop.gremlin.structure.io.IoRegistry} interface that provides serializers with custom configurations for
- * implementation specific classes that might need to be serialized.  This registry allows a {@link org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph} to
+ * An implementation of the {@link IoRegistry} interface that provides serializers with custom configurations for
+ * implementation specific classes that might need to be serialized.  This registry allows a {@link TinkerGraph} to
  * be serialized directly which is useful for moving small graphs around on the network.
  * <p/>
  * Most providers need not implement this kind of custom serializer as they will deal with much larger graphs that

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerTest.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerTest.java
index 3decd5f..703d127 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerTest.java
@@ -57,7 +57,6 @@ public class TinkerGraphGryoSerializerTest {
 
     @Before
     public void setUp() throws Exception {
-
         when(kryo.getRegistration((Class) any())).thenReturn(registration);
         when(input.readBytes(anyInt())).thenReturn(Arrays.copyOf(GryoMapper.HEADER, 100));
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV1d0Test.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV1d0Test.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV1d0Test.java
new file mode 100644
index 0000000..2233d53
--- /dev/null
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV1d0Test.java
@@ -0,0 +1,83 @@
+/*
+ * 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.tinkergraph.structure;
+
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoMapper;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistry;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0;
+import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Registration;
+import org.apache.tinkerpop.shaded.kryo.io.Input;
+import org.apache.tinkerpop.shaded.kryo.io.Output;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.util.Arrays;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link TinkerIoRegistry.TinkerGraphGryoSerializer}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class TinkerGraphGryoSerializerV1d0Test {
+
+    @Mock
+    private Kryo kryo;
+    @Mock
+    private Registration registration;
+    @Mock
+    private Output output;
+    @Mock
+    private Input input;
+
+    private TinkerGraph graph = TinkerGraph.open();
+    private TinkerIoRegistryV1d0.TinkerGraphGryoSerializer serializer = new TinkerIoRegistryV1d0.TinkerGraphGryoSerializer();
+
+    @Before
+    public void setUp() throws Exception {
+        when(kryo.getRegistration((Class) any())).thenReturn(registration);
+        when(input.readBytes(anyInt())).thenReturn(Arrays.copyOf(GryoMapper.HEADER, 100));
+    }
+
+    @Test
+    public void shouldVerifyKryoUsedForWrite() throws Exception {
+        serializer.write(kryo, output, graph);
+        verify(kryo, atLeastOnce()).getRegistration((Class) any());
+    }
+
+    @Test
+    public void shouldVerifyKryoUsedForRead() throws Exception {
+        // Not possible to mock an entire deserialization so just verify the same kryo instances are being used
+        try {
+            serializer.read(kryo, input, TinkerGraph.class);
+        } catch (RuntimeException ex) {
+            verify(kryo, atLeastOnce()).readObject(any(), any());
+            verify(kryo, atLeastOnce()).readClassAndObject(any());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV2d0Test.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV2d0Test.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV2d0Test.java
new file mode 100644
index 0000000..7d2a5e3
--- /dev/null
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphGryoSerializerV2d0Test.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tinkergraph.structure;
+
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoMapper;
+import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Registration;
+import org.apache.tinkerpop.shaded.kryo.io.Input;
+import org.apache.tinkerpop.shaded.kryo.io.Output;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.util.Arrays;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link TinkerIoRegistry.TinkerGraphGryoSerializer}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class TinkerGraphGryoSerializerV2d0Test {
+
+    @Mock
+    private Kryo kryo;
+    @Mock
+    private Registration registration;
+    @Mock
+    private Output output;
+    @Mock
+    private Input input;
+
+    private TinkerGraph graph = TinkerGraph.open();
+    private TinkerIoRegistryV2d0.TinkerGraphGryoSerializer serializer = new TinkerIoRegistryV2d0.TinkerGraphGryoSerializer();
+
+    @Before
+    public void setUp() throws Exception {
+        when(kryo.getRegistration((Class) any())).thenReturn(registration);
+        when(input.readBytes(anyInt())).thenReturn(Arrays.copyOf(GryoMapper.HEADER, 100));
+    }
+
+    @Test
+    public void shouldVerifyKryoUsedForWrite() throws Exception {
+        serializer.write(kryo, output, graph);
+        verify(kryo, atLeastOnce()).getRegistration((Class) any());
+    }
+
+    @Test
+    public void shouldVerifyKryoUsedForRead() throws Exception {
+        // Not possible to mock an entire deserialization so just verify the same kryo instances are being used
+        try {
+            serializer.read(kryo, input, TinkerGraph.class);
+        } catch (RuntimeException ex) {
+            verify(kryo, atLeastOnce()).readObject(any(), any());
+            verify(kryo, atLeastOnce()).readClassAndObject(any());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/802ff9f6/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
index 9de9a70..a85b0ee 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
@@ -546,7 +546,35 @@ public class TinkerGraphTest {
         final ArrayList<Color> colorList = new ArrayList<>(Arrays.asList(Color.RED, Color.GREEN));
 
         final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier();
-        final GryoMapper mapper = GryoMapper.build().addRegistry(TinkerIoRegistry.instance()).classResolver(classResolver).create();
+        final GryoMapper mapper = GryoMapper.build().addRegistry(TinkerIoRegistryV1d0.instance()).classResolver(classResolver).create();
+        final Kryo kryo = mapper.createMapper();
+        try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            final Output out = new Output(stream);
+
+            kryo.writeObject(out, colorList);
+            out.flush();
+            final byte[] b = stream.toByteArray();
+
+            try (final InputStream inputStream = new ByteArrayInputStream(b)) {
+                final Input input = new Input(inputStream);
+                final List m = kryo.readObject(input, ArrayList.class);
+                final TinkerGraph readX = (TinkerGraph) m.get(0);
+                assertEquals(104, IteratorUtils.count(readX.vertices()));
+                assertEquals(102, IteratorUtils.count(readX.edges()));
+            }
+        }
+    }
+
+    @Test
+    public void shouldSerializeWithColorClassResolverToTinkerGraphUsingDeprecatedTinkerIoRegistry() throws Exception {
+        final Map<String,Color> colors = new HashMap<>();
+        colors.put("red", Color.RED);
+        colors.put("green", Color.GREEN);
+
+        final ArrayList<Color> colorList = new ArrayList<>(Arrays.asList(Color.RED, Color.GREEN));
+
+        final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier();
+        final GryoMapper mapper = GryoMapper.build().addRegistry(TinkerIoRegistryV1d0.instance()).classResolver(classResolver).create();
         final Kryo kryo = mapper.createMapper();
         try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
             final Output out = new Output(stream);


[19/24] tinkerpop git commit: optimized math examples

Posted by sp...@apache.org.
optimized math examples


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

Branch: refs/heads/TINKERPOP-1600
Commit: 9cdc47c3876ab3326de096a66228ccd43d8a42ca
Parents: f992cdf
Author: Daniel Kuppitz <da...@hotmail.com>
Authored: Tue Jan 17 17:52:45 2017 +0100
Committer: Daniel Kuppitz <da...@hotmail.com>
Committed: Tue Jan 17 17:52:45 2017 +0100

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/9cdc47c3/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index e9d13aa..55fa45d 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -169,9 +169,9 @@ _Methods for performing some basic mathematical operations in the "modern" graph
 ----
 g.V().values("age").sum() // sum all ages
 g.V().values("age").fold(1, mult) // multiply all ages
-g.V().values("age").map(union(identity(), constant(-1)).sum()) // subtract 1
-g.V().values("age").map(union(identity(), identity()).sum()) // multiply by 2 (simple)
-g.V().values("age").map(union(identity(), constant(2)).fold(1, mult)) // multiply by 2 (generally useful for multiplications by n):
+g.withSack(0).V().values("age").sack(sum).sack(sum).by(constant(-1)).sack() // subtract 1
+g.withSack(0).V().values("age").sack(sum).sack(sum).sack() // multiply by 2 (simple)
+g.withSack(0).V().values("age").sack(sum).sack(mult).by(constant(2)).sack() // multiply by 2 (generally useful for multiplications by n)
 ----
 
 [[appendix-g]]
@@ -259,4 +259,4 @@ g.V().as("a").
     group().
       by(select(keys).unfold()).
       by(select(values).unfold().unfold().fold()))
-----
\ No newline at end of file
+----


[07/24] tinkerpop git commit: Added duplicate edge detection recipe

Posted by sp...@apache.org.
Added duplicate edge detection recipe


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

Branch: refs/heads/TINKERPOP-1600
Commit: 7783c838e15cfdf3ee567790c657f34403766936
Parents: 043d1a3
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 08:49:02 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 08:49:02 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/duplicate-edge.asciidoc | 142 ++++++++++++++++++++++++++
 docs/src/recipes/index.asciidoc          |  18 ++++
 2 files changed, 160 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7783c838/docs/src/recipes/duplicate-edge.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/duplicate-edge.asciidoc b/docs/src/recipes/duplicate-edge.asciidoc
new file mode 100644
index 0000000..ed56106
--- /dev/null
+++ b/docs/src/recipes/duplicate-edge.asciidoc
@@ -0,0 +1,142 @@
+////
+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.
+////
+[[duplicate-edge]]
+Duplicate Edge Detection
+------------------------
+
+Whether part of a graph maintenance process or for some other analysis need, it is sometimes necessary to detect
+if there is more than one edge between two vertices. The following examples will assume that an edge with the same
+label and direction will be considered "duplicate".
+
+The "modern" graph does not have any duplicate edges that fit that definition, so the following example adds one
+that is duplicative of the "created" edge between vertex "1" and "3".
+
+[gremlin-groovy,modern]
+----
+g.V(1).as("a").V(3).addE("created").from("a").iterate()
+g.V(1).outE("created")
+----
+
+One way to find the duplicate edges would be to do something like this:
+
+[gremlin-groovy,existing]
+----
+g.V().outE().
+  project("a","b").                         <1>
+    by().by(inV().path().by().by(label)).
+  group().                                  <2>
+    by(select("b")).
+    by(select("a").fold()).
+  unfold().                                 <3>
+  select(values).                           <4>
+  where(count(local).is(gt(1)))
+----
+
+<1> The "a" and "b" from the `project` contain the edge and the path respectively. The path consists of a the outgoing
+vertex, an edge, and the incoming vertex. The use of `by().by(label))` converts the edge to its label (recall that `by`
+are applied in round-robin fashion), so the path will look something like: `[v[1],created,v[3]]`.
+<2> Group by the path from "b" and construct a list of edges from "a". Any value in this `Map` that has a list of edges
+greater than one means that there is more than one edge for that edge label between those two vertices (i.e. the `Map`
+key).
+<3> Unroll the key-value pairs in the `Map` of paths-edges.
+<4> Only the values from the `Map` are needed and as mentioned earlier, those lists with more than one edge would
+containa  duplicate.
+
+This method find the duplicates, but does require more memory than other approaches. A slightly more complex approach
+that uses less memory might look like this:
+
+[gremlin-groovy,existing]
+----
+g.V().as("ov").
+  outE().as("e").
+  inV().as("iv").
+  inE().                                          <1>
+  where(neq("e")).                                <2>
+  where(eq("e")).by(label).
+  where(outV().as("ov")).
+  group().
+    by(select("ov","e","iv").by().by(label)).     <3>
+  unfold().                                       <4>
+  select(values).
+  where(count(local).is(gt(1)))
+----
+
+<1> To this point in the traversal, the outgoing edges of a vertex are being iterated with the current edge labelled
+as "e". For "e", Gremlin traverses to the incoming vertex and back on in edges of that vertex.
+<2> Those incoming edges are filtered with the following `where` steps. The first ensures that it does not traverse
+back over "e" (i.e. the current edge). The second determines if the edge label is equivalent (i.e. the test for the
+working definition of "duplicate"). The third determines if the outgoing vertex matches the one that started the
+path labelled as "ov".
+<3> This line is quite similar to the output achieved in the previous example at step 2. A `Map` is produced that uses
+the outgoing vertex, the edge label, and the incoming vertex as the key, with the list of edges for that path as the
+value.
+<4> The rest of the traversal is the same as the previous one.
+
+A third way to approach this problem would be to force a link:https://en.wikipedia.org/wiki/Depth-first_search[depth-first search].
+The previous examples invoke traversal strategies that force a link:https://en.wikipedia.org/wiki/Breadth-first_search[breadth first search]
+as a performance optimization.
+
+[gremlin-groovy,existing]
+----
+g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as("ov").   <1>
+  outE().as("e1").
+  inV().as("iv").
+  inE().
+  where(neq("e1")).
+  where(outV().as("ov")).as("e2").                                               <2>
+  filter(select("e1","e2").by(label).where("e1", eq("e2")))                      <3>
+----
+
+<1> Remove strategies that will optimize for breadth first searches and thus allow Gremlin to go depth first.
+<2> To this point, the traversal is very much like the previous one. Review step 2 in the previous example to see the
+parallels here.
+<3> The final `filter` simply looks for edges that match on label, which would then meet the working definition of
+"duplicate".
+
+The basic pattern at play here is to compare the path of the outgoing vertex, its outgoing edge label and the incoming
+vertex. This model can obviously be contracted or expanded as needed to fit different definitions of "duplicate" For
+example, if the definition of "duplicate" was just two vertices with more than one edge of any label between them, then
+the approach might look something like this:
+
+[gremlin-groovy,existing]
+----
+----
+
+Alternatively, a "duplicate" definition could extended to the label and properties of the edge. For purposes of
+demonstration, an additional edge is added to the "modern" graph:
+
+[gremlin-groovy]
+----
+g = TinkerFactory.createModern().traversal()
+g.V(1).as("a").V(3).addE("created").property("weight",0.4d).from("a").iterate()
+g.V(1).as("a").V(3).addE("created").property("weight",0.5d).from("a").iterate()
+g.V(1).outE("created").valueMap(true)
+----
+
+To identify the duplicate with this revised definition, the previous traversal can be modified to:
+
+[gremlin-groovy,existing]
+----
+g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as("ov").
+  outE().as("e1").
+  inV().as("iv").
+  inE().
+  where(neq("e1")).
+  where(outV().as("ov")).as("e2").
+  filter(select("e1","e2").by(label).where("e1", eq("e2"))).
+  filter(select("e1","e2").by("weight").where("e1", eq("e2"))).valueMap(true)
+----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7783c838/docs/src/recipes/index.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/index.asciidoc b/docs/src/recipes/index.asciidoc
index 2e88ac5..9c42f0f 100644
--- a/docs/src/recipes/index.asciidoc
+++ b/docs/src/recipes/index.asciidoc
@@ -44,6 +44,8 @@ include::connected-components.asciidoc[]
 
 include::cycle-detection.asciidoc[]
 
+include::duplicate-edge.asciidoc[]
+
 include::if-then-based-grouping.asciidoc[]
 
 include::pagination.asciidoc[]
@@ -212,4 +214,20 @@ g.V().hasLabel("student").as("s").
     by(select("s").by("name")).
     by(group().by(select("t").by(valueMap("fromTime","toTime"))).
                by(select("c").dedup().values("name").fold())).next()
+----
+
+[[appendix-d]]
+_In the "modern" graph, with a duplicate edge added, find the vertex pairs that have more than one edge between them._
+
+[gremlin-groovy,modern]
+----
+g.V(1).as("a").V(3).addE("created").property("weight",0.4d).from("a").iterate()
+g.V(1).outE("created")
+g.V().as("a").
+  out().as("b").
+  groupCount().
+    by(select("a","b")).
+  unfold().
+  filter(select(values).is(gt(1))).
+  select(keys)
 ----
\ No newline at end of file


[08/24] tinkerpop git commit: Move appendix to its own doc and out of index.

Posted by sp...@apache.org.
Move appendix to its own doc and out of index.


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

Branch: refs/heads/TINKERPOP-1600
Commit: 3e54d897e70f26fdefbe30daf5ce19a38a052e4d
Parents: 7783c83
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 08:52:58 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 08:52:58 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 112 ++++++++++++++++++++++++++++++++
 docs/src/recipes/index.asciidoc    |  97 +--------------------------
 2 files changed, 113 insertions(+), 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3e54d897/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
new file mode 100644
index 0000000..63ec447
--- /dev/null
+++ b/docs/src/recipes/appendix.asciidoc
@@ -0,0 +1,112 @@
+////
+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.
+////
+Appendix
+========
+
+Many of the recipes are based on questions and answers provided on the gremlin-users mailing list. This section
+contains a number of traversals from the mailing list that do not easily fit any particular pattern (i.e. a recipe),
+but are nonetheless interesting and thus remain good tools for learning Gremlin.
+
+[[appendix-a]]
+_For each person in a "follows" graph, determine the number of followers and list their names._
+
+[gremlin-groovy]
+----
+g.addV('name','marko').as('marko').
+  addV('name','josh').as('josh').
+  addV('name','daniel').as('daniel').
+  addV('name','matthias').as('matthias').
+  addE('follows').from('josh').to('marko').
+  addE('follows').from('matthias').to('josh').
+  addE('follows').from('daniel').to('josh').
+  addE('follows').from('daniel').to('marko').iterate()
+g.V().as('p').
+  map(__.in('follows').values('name').fold()).
+  group().by(select('p').by('name')).
+          by(project('numFollowers','followers').
+               by(count(local)).by()).next()
+----
+
+[[appendix-b]]
+_In the "modern" graph, show each person, the software they worked on and the co-worker count for the software and
+the names of those co-workers._
+
+[gremlin-groovy,modern]
+----
+g.V().hasLabel("person").as("p").
+  out("created").as("s").
+  map(__.in("created").
+    where(neq("p")).values("name").fold()).
+  group().by(select("p").by("name")).
+    by(group().by(select("s").by("name")).
+    by(project("numCoworkers","coworkers").
+         by(count(local)).by())).next()
+----
+
+[[appendix-c]]
+_Assuming a graph of students, classes and times, detect students who have a conflicting schedule._
+
+[gremlin-groovy]
+----
+g.addV(label, "student", "name", "Pete").as("s1").
+  addV(label, "student", "name", "Joe").as("s2").
+  addV(label, "class", "name", "Java's GC").as("c1").
+  addV(label, "class", "name", "FP Principles").as("c2").
+  addV(label, "class", "name", "Memory Management in C").as("c3").
+  addV(label, "class", "name", "Memory Management in C++").as("c4").
+  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "10:00", "toTime", "11:00").as("t1").
+  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "11:00", "toTime", "12:00").as("t2").
+  addE("attends").from("s1").to("c1").
+  addE("attends").from("s1").to("c2").
+  addE("attends").from("s1").to("c3").
+  addE("attends").from("s1").to("c4").
+  addE("attends").from("s2").to("c2").
+  addE("attends").from("s2").to("c3").
+  addE("allocated").from("c1").to("t1").
+  addE("allocated").from("c1").to("t2").
+  addE("allocated").from("c2").to("t1").
+  addE("allocated").from("c3").to("t2").
+  addE("allocated").from("c4").to("t2").iterate()
+g.V().hasLabel("student").as("s").
+  out("attends").as("c").
+  out("allocated").as("t").
+  select("s").
+  out("attends").
+  where(neq("c")).
+  out("allocated").
+  where(eq("t")).
+  group().
+    by(select("s").by("name")).
+    by(group().by(select("t").by(valueMap("fromTime","toTime"))).
+               by(select("c").dedup().values("name").fold())).next()
+----
+
+[[appendix-d]]
+_In the "modern" graph, with a duplicate edge added, find the vertex pairs that have more than one edge between them._
+
+[gremlin-groovy,modern]
+----
+g.V(1).as("a").V(3).addE("created").property("weight",0.4d).from("a").iterate()
+g.V(1).outE("created")
+g.V().as("a").
+  out().as("b").
+  groupCount().
+    by(select("a","b")).
+  unfold().
+  filter(select(values).is(gt(1))).
+  select(keys)
+----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3e54d897/docs/src/recipes/index.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/index.asciidoc b/docs/src/recipes/index.asciidoc
index 9c42f0f..f77b929 100644
--- a/docs/src/recipes/index.asciidoc
+++ b/docs/src/recipes/index.asciidoc
@@ -135,99 +135,4 @@ GitHub and JIRA are linked.  As mentioned earlier in this section, the recipe wi
 committers prior to merging. This process may take several days to complete. We look forward to receiving your
 submissions!
 
-Appendix
-========
-
-Many of the recipes are based on questions and answers provided on the gremlin-users mailing list. This section
-contains a number of traversals from the mailing list that do not easily fit any particular pattern (i.e. a recipe),
-but are nonetheless interesting and thus remain good tools for learning Gremlin.
-
-[[appendix-a]]
-_For each person in a "follows" graph, determine the number of followers and list their names._
-
-[gremlin-groovy]
-----
-g.addV('name','marko').as('marko').
-  addV('name','josh').as('josh').
-  addV('name','daniel').as('daniel').
-  addV('name','matthias').as('matthias').
-  addE('follows').from('josh').to('marko').
-  addE('follows').from('matthias').to('josh').
-  addE('follows').from('daniel').to('josh').
-  addE('follows').from('daniel').to('marko').iterate()
-g.V().as('p').
-  map(__.in('follows').values('name').fold()).
-  group().by(select('p').by('name')).
-          by(project('numFollowers','followers').
-               by(count(local)).by()).next()
-----
-
-[[appendix-b]]
-_In the "modern" graph, show each person, the software they worked on and the co-worker count for the software and
-the names of those co-workers._
-
-[gremlin-groovy,modern]
-----
-g.V().hasLabel("person").as("p").
-  out("created").as("s").
-  map(__.in("created").
-    where(neq("p")).values("name").fold()).
-  group().by(select("p").by("name")).
-    by(group().by(select("s").by("name")).
-    by(project("numCoworkers","coworkers").
-         by(count(local)).by())).next()
-----
-
-[[appendix-c]]
-_Assuming a graph of students, classes and times, detect students who have a conflicting schedule._
-
-[gremlin-groovy]
-----
-g.addV(label, "student", "name", "Pete").as("s1").
-  addV(label, "student", "name", "Joe").as("s2").
-  addV(label, "class", "name", "Java's GC").as("c1").
-  addV(label, "class", "name", "FP Principles").as("c2").
-  addV(label, "class", "name", "Memory Management in C").as("c3").
-  addV(label, "class", "name", "Memory Management in C++").as("c4").
-  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "10:00", "toTime", "11:00").as("t1").
-  addV(label, "timeslot", "date", "11/25/2016", "fromTime", "11:00", "toTime", "12:00").as("t2").
-  addE("attends").from("s1").to("c1").
-  addE("attends").from("s1").to("c2").
-  addE("attends").from("s1").to("c3").
-  addE("attends").from("s1").to("c4").
-  addE("attends").from("s2").to("c2").
-  addE("attends").from("s2").to("c3").
-  addE("allocated").from("c1").to("t1").
-  addE("allocated").from("c1").to("t2").
-  addE("allocated").from("c2").to("t1").
-  addE("allocated").from("c3").to("t2").
-  addE("allocated").from("c4").to("t2").iterate()
-g.V().hasLabel("student").as("s").
-  out("attends").as("c").
-  out("allocated").as("t").
-  select("s").
-  out("attends").
-  where(neq("c")).
-  out("allocated").
-  where(eq("t")).
-  group().
-    by(select("s").by("name")).
-    by(group().by(select("t").by(valueMap("fromTime","toTime"))).
-               by(select("c").dedup().values("name").fold())).next()
-----
-
-[[appendix-d]]
-_In the "modern" graph, with a duplicate edge added, find the vertex pairs that have more than one edge between them._
-
-[gremlin-groovy,modern]
-----
-g.V(1).as("a").V(3).addE("created").property("weight",0.4d).from("a").iterate()
-g.V(1).outE("created")
-g.V().as("a").
-  out().as("b").
-  groupCount().
-    by(select("a","b")).
-  unfold().
-  filter(select(values).is(gt(1))).
-  select(keys)
-----
\ No newline at end of file
+include::appendix.asciidoc[]
\ No newline at end of file


[10/24] tinkerpop git commit: Added a recipe appendix entry

Posted by sp...@apache.org.
Added a recipe appendix entry


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

Branch: refs/heads/TINKERPOP-1600
Commit: 0938ebdce4b6d8512674503680c660eea9870e9e
Parents: d7ecfc0
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 10 10:45:56 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 10 10:45:56 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0938ebdc/docs/src/recipes/appendix.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/appendix.asciidoc b/docs/src/recipes/appendix.asciidoc
index 63ec447..15d7340 100644
--- a/docs/src/recipes/appendix.asciidoc
+++ b/docs/src/recipes/appendix.asciidoc
@@ -109,4 +109,19 @@ g.V().as("a").
   unfold().
   filter(select(values).is(gt(1))).
   select(keys)
-----
\ No newline at end of file
+----
+
+[[appendix-e]]
+_In the "crew" graph, find vertices that match on a complete set of multi-properties._
+
+[gremlin-groovy,theCrew]
+----
+places = ["centreville","dulles"];[]  // will not match as "purcellville" is missing
+g.V().not(has("location", without(places))).
+  where(values("location").is(within(places)).count().is(places.size())).
+  valueMap()
+places = ["centreville","dulles","purcellville"];[]
+g.V().not(has("location", without(places))).
+  where(values("location").is(within(places)).count().is(places.size())).
+  valueMap()
+----


[17/24] tinkerpop git commit: Allow some extra time to be sure the future completes

Posted by sp...@apache.org.
Allow some extra time to be sure the future completes

This test never fails on my system but on rare occassions it seems to fail for others. I can only guess it is a timing issue of sorts, but I'm not sure I see how to better test this. The point of the test is not related to timing, so adding this extra wait doesn't seem wrong. It's not as though the functionality is not being tested properly. We just need to validate that the future completes when all the items arrive to the ResultSet, so in that sense the addition of the wait also validates that. CTR


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

Branch: refs/heads/TINKERPOP-1600
Commit: 1e856b344849fe7db4ba2ce8e4ccdd9e0de53cb8
Parents: 9d1c3e5
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 09:25:23 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 09:25:23 2017 -0500

----------------------------------------------------------------------
 .../java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java  | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1e856b34/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java
index 4a57ebb..0cf4fb5 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultSetTest.java
@@ -26,6 +26,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Stream;
@@ -76,6 +77,9 @@ public class ResultSetTest extends AbstractResultQueueTest {
         }
 
         assertThat(atLeastOnce.get(), is(true));
+
+        // ensure there is enough time for the readComplete to complete the "all" future
+        all.get(30000, TimeUnit.MILLISECONDS);
         assertThat(all.isDone(), is(true));
     }
 


[05/24] tinkerpop git commit: Added connected components recipe

Posted by sp...@apache.org.
Added connected components recipe


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

Branch: refs/heads/TINKERPOP-1600
Commit: d5404b8166525f0dd62d0e0bde3cce9fc68cd887
Parents: 52f7f96
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Jan 9 11:12:03 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Jan 9 11:12:03 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/connected-components.asciidoc |  82 ++++++++++++++++++++
 docs/src/recipes/index.asciidoc                |   2 +
 docs/static/images/connected-components.png    | Bin 0 -> 17946 bytes
 3 files changed, 84 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d5404b81/docs/src/recipes/connected-components.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/connected-components.asciidoc b/docs/src/recipes/connected-components.asciidoc
new file mode 100644
index 0000000..cfb93fb
--- /dev/null
+++ b/docs/src/recipes/connected-components.asciidoc
@@ -0,0 +1,82 @@
+////
+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.
+////
+[[connected-components]]
+Connected Components
+--------------------
+
+Gremlin can be used to find link:https://en.wikipedia.org/wiki/Connected_component_(graph_theory)[connected components]
+in a graph. Consider the following graph which has three connected components:
+
+image:connected-components.png[width=600]
+
+[gremlin-groovy]
+----
+g.addV(id, "A").as("a").
+  addV(id, "B").as("b").
+  addV(id, "C").as("c").
+  addV(id, "D").as("d").
+  addV(id, "E").as("e").
+  addV(id, "F").
+  addE("link").from("a").to("b").
+  addE("link").from("b").to("c").
+  addE("link").from("d").to("e").iterate()
+----
+
+One way to detect the various subgraphs would be to do something like this:
+
+[gremlin-groovy,existing]
+----
+g.V().emit(cyclicPath().or().not(both())).repeat(both()).until(cyclicPath()).  <1>
+  aggregate("p").by(path()).cap("p").                                          <2>
+  unfold().limit(local, 1).dedup().                                            <3>
+  map(__.as("v").select("p").unfold().                                         <4>
+         filter(unfold().where(eq("v"))).
+         unfold().dedup().order().by(id).fold()
+  ).dedup()                                                                    <5>
+----
+
+<1> Iterate all vertices and repeatedly traverse over both incoming and outgoing edges (TinkerPop doesn't support
+unidirectional graphs directly so it must be simulated by ignoring the direction with `both`). Note the use of `emit`
+prior to `repeat` as this allows for return of a single length path.
+<2> Aggregate the emitted vertices to "p" and get their path information. Calling `cap` at the end will push the
+aggregate path list into the traversal. It is within these paths that the list of connected components will be
+identified. Obviously the paths list are duplicative in the sense that they contains different paths travelled over
+the same vertices.
+<3> Unroll the elements in the path list with `unfold` and grab the first vertex in each path and `dedup`.
+<4> Use the first vertex in each path to filter against the paths stored in "p". When a path is found that has the
+vertex in it, dedup the vertices in the path, order it by the identifier. Each path output from this `map` step
+represents a connected component.
+<5> The connected component list is duplicative given the nature of the paths in "p", but now that the vertices within
+the paths are ordered, a final `dedup` will make the list of connective components unique.
+
+NOTE: This is a nice example of where running smaller pieces of a large Gremlin statement make it easier to see what
+is happening at each step. Consider running this example one line at a time (or perhaps even in a step at a time) to
+see the output at each point.
+
+While the above approach returns results nicely, the traversal doesn't appear to work with OLAP. A less efficient
+approach, but one more suited for OLAP execution looks quite similar but does not use `dedup` as heavily (thus
+`GraphComputer` is forced to analyze far more paths):
+
+[gremlin-groovy,existing]
+----
+g.withComputer().V().emit(cyclicPath().or().not(both())).repeat(both()).until(cyclicPath()).
+  aggregate("p").by(path()).cap("p").unfold().limit(local, 1).
+  map(__.as("v").select("p").unfold().
+         filter(unfold().where(eq("v"))).
+         unfold().dedup().order().by(id).fold()
+  ).toSet()
+----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d5404b81/docs/src/recipes/index.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/index.asciidoc b/docs/src/recipes/index.asciidoc
index 47c2b37..8ffc358 100644
--- a/docs/src/recipes/index.asciidoc
+++ b/docs/src/recipes/index.asciidoc
@@ -40,6 +40,8 @@ include::between-vertices.asciidoc[]
 
 include::centrality.asciidoc[]
 
+include::connected-components.asciidoc[]
+
 include::cycle-detection.asciidoc[]
 
 include::if-then-based-grouping.asciidoc[]

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d5404b81/docs/static/images/connected-components.png
----------------------------------------------------------------------
diff --git a/docs/static/images/connected-components.png b/docs/static/images/connected-components.png
new file mode 100755
index 0000000..ef3cc41
Binary files /dev/null and b/docs/static/images/connected-components.png differ


[20/24] tinkerpop git commit: Merge remote-tracking branch 'origin/more-recipes' into tp32

Posted by sp...@apache.org.
Merge remote-tracking branch 'origin/more-recipes' into tp32


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

Branch: refs/heads/TINKERPOP-1600
Commit: e06ce0c19bb7d1752db8bd578e97f4b11e6e6ea8
Parents: 1e856b3 9cdc47c
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 12:22:45 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 12:22:45 2017 -0500

----------------------------------------------------------------------
 docs/src/recipes/appendix.asciidoc              | 262 +++++++++++++++++++
 docs/src/recipes/connected-components.asciidoc  |  82 ++++++
 docs/src/recipes/cycle-detection.asciidoc       |  55 ++++
 docs/src/recipes/duplicate-edge.asciidoc        | 160 +++++++++++
 docs/src/recipes/duplicate-vertex.asciidoc      |  52 ++++
 docs/src/recipes/index.asciidoc                 |   8 +-
 .../recipes/traversal-induced-values.asciidoc   | 109 ++++++++
 docs/src/reference/the-traversal.asciidoc       |   4 +
 docs/static/images/connected-components.png     | Bin 0 -> 17946 bytes
 docs/static/images/eulerian-circuit.png         | Bin 0 -> 25270 bytes
 .../images/traversal-induced-values-1.png       | Bin 0 -> 12607 bytes
 .../images/traversal-induced-values-2.png       | Bin 0 -> 40440 bytes
 .../images/traversal-induced-values-3.png       | Bin 0 -> 49652 bytes
 13 files changed, 731 insertions(+), 1 deletion(-)
----------------------------------------------------------------------