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/17 17:23:39 UTC

[01/18] tinkerpop git commit: Added more cycle detection and traversal induced value recipes.

Repository: tinkerpop
Updated Branches:
  refs/heads/master b2866d453 -> c328dac3a


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/master
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


[07/18] 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/master
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


[14/18] 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/master
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._
 


[11/18] 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/master
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


[06/18] 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/master
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


[13/18] 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/master
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/18] 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/master
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


[02/18] 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/master
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


[09/18] 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/master
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()
+----


[16/18] 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/master
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
+----


[12/18] 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/master
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._


[05/18] 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/master
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


[18/18] tinkerpop git commit: Merge branch 'tp32'

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


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

Branch: refs/heads/master
Commit: c328dac3af7860477f812f1a2e853d7122929d4d
Parents: b2866d4 e06ce0c
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Jan 17 12:23:25 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Jan 17 12:23:25 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(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/c328dac3/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------


[15/18] 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/master
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:


[03/18] 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/master
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]]


[10/18] 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/master
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


[17/18] 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/master
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(-)
----------------------------------------------------------------------



[08/18] 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/master
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[]