You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tinkerpop.apache.org by spmallette <gi...@git.apache.org> on 2017/01/10 18:50:47 UTC

[GitHub] tinkerpop pull request #531: Added more recipes

GitHub user spmallette opened a pull request:

    https://github.com/apache/tinkerpop/pull/531

    Added more recipes

    There's no JIRA ticket for this. It's just a random bunch of additions to the recipes documentation. Note that in this update i added an "Appendix" which simply contains some "complex" or otherwise "interesting" traversals that don't seem to fit a pattern/recipe, but remain potentially useful for learning.
    
    I've published the docs here for review:
    
    http://tinkerpop.apache.org/docs/3.2.4-SNAPSHOT/recipes/
    
    VOTE +1

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/apache/tinkerpop more-recipes

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/tinkerpop/pull/531.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #531
    
----

----


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95564117
  
    --- Diff: 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()).
    --- End diff --
    
    I ran your "OLTP" traversal `withComputer()` and it works as expected.. ?
    
    ```
    gremlin> g.withComputer().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"))).
    ......5>          unfold().dedup().order().by(id).fold()
    ......6>   ).dedup()
    ==>[v[A],v[B],v[C]]
    ==>[v[D],v[E]]
    ==>[v[F]]
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95562939
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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').
    --- End diff --
    
    You could also show another version.
    
    ```
    g.V().group().
            by('name').
            by(project('numFollowers','followers').
                 by(__.in('follows').count()).
                 by(__.in('follows').values('name').fold())).next()
    ```
    
    or yet another version....
    
    ```
    g.V().group().
            by('name').
            by(__.in('follows').values('name').fold().
                project('numFollowers','followers').
                  by(count(local)).
                  by()).next()
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95638679
  
    --- Diff: 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()).
    --- End diff --
    
    I'm pretty sure that's not working in `tp32` (OLAP).


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95563597
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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)
    +----
    +
    +[[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()
    +----
    +
    +[[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):
    +----
    +
    +[[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()))
    +----
    --- End diff --
    
    I'm wondering if some of these traversals may "look better" with `match()`. @dkuppitz ?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop issue #531: Added more recipes

Posted by spmallette <gi...@git.apache.org>.
Github user spmallette commented on the issue:

    https://github.com/apache/tinkerpop/pull/531
  
    I'm not so sure how good I am at Gremlin - a good portion of this stuff comes from @dkuppitz answers :smile: - but I'm better at Gremlin for sure after writing these recipes as I'm forced to really analyze and question what's going on in the traversals so that I can write a proper narrative. Of course, it still is a fair bit of work to write all this up, organize it into patterns, sometimes re-write traversals or data to better fit the narrative, etc. so I won't argue about that.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95565322
  
    --- Diff: 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").
    --- End diff --
    
    Here is the `match()`-form.
    
    ```
    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)))
     ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by spmallette <gi...@git.apache.org>.
Github user spmallette commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95781687
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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
    --- End diff --
    
    i misread - got it


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop issue #531: Added more recipes

Posted by spmallette <gi...@git.apache.org>.
Github user spmallette commented on the issue:

    https://github.com/apache/tinkerpop/pull/531
  
    Thanks for all the feedback. I've pushed some changes on this branch given the comments I've seen. I think I got all of it. Good to vote now?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95639012
  
    --- Diff: 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").
    --- End diff --
    
    Go home.........


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop issue #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on the issue:

    https://github.com/apache/tinkerpop/pull/531
  
    VOTE +1.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by spmallette <gi...@git.apache.org>.
Github user spmallette commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95641629
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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)
    +----
    +
    +[[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()
    +----
    +
    +[[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):
    +----
    +
    +[[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()))
    +----
    --- End diff --
    
    I wonder if our lack of and extensive set of  `match()` examples continues to create the sense that we don't have a declarative story for Gremlin. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95785972
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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._
    --- End diff --
    
    I think it's important to note that this is only for edge that point into the same direction (`OUT` in the example). The solution for undirected edges is pretty complicated (if we take into account that ids are not necessarily sortable).
    
    ```
    g.V().as("a").both().as("b").
      groupCount().by(select("a","b")).unfold().
      filter(select(values).is(gt(1))).
      select(keys).not(__.as("x").select("done").unfold().as("y").
                       select("x","y").by(select("a")).by(select("b")).
                       where("x", eq("y"))).store("done")
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/tinkerpop/pull/531


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95783275
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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').
    --- End diff --
    
    `group()` is actually not needed here. `p` will be unique in the result, hence it's just a projection:
    
    ```
    g.V().as('p').map(__.in('follows').values('name').fold()).
      project('person','followers','numFollowers').by(select('p')).by('name')).by().by(count(local))
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95637868
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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)
    +----
    +
    +[[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()
    +----
    +
    +[[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):
    +----
    +
    +[[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()))
    +----
    --- End diff --
    
    Do we want to look good or perform good? You may have noticed it on the mailing list, I always try to prevent the use of `match()`. There were only a few questions that I ever answered with a match query and that was only for the sake of readability.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by okram <gi...@git.apache.org>.
Github user okram commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95562174
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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
    --- End diff --
    
    You should `href` to gremlin-users mailing list. https://groups.google.com/forum/#!forum/gremlin-users


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop issue #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on the issue:

    https://github.com/apache/tinkerpop/pull/531
  
    I went through all the code snippets again, optimized a few math examples and I think it's all looking good now.
    
    VOTE: +1


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by spmallette <gi...@git.apache.org>.
Github user spmallette commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95640133
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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
    --- End diff --
    
    I debated on whether to do that or not with recipes in general but given that I usually adapt the traversals to a narrative, alter the data or improve upon them from a performance perspective or usability perspective linking back doesn't always make sense, so rather than do it some of the times, I opted to not do it at all. The "appendix" might be different though....I don't recall if I made any modifications to those or just "copied" them writing my own explanation for what they were doing as a "title". Maybe adding the links for those would be useful in some way....let me think on that.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95641135
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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
    --- End diff --
    
    I believe what @okram meant is just one link, like:
    
    > Many of the recipes are based on questions and answers provided on the [gremlin-users mailing list](https://groups.google.com/forum/#!forum/gremlin-users). This section...
    
    Not a link to every discussion.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] tinkerpop pull request #531: Added more recipes

Posted by dkuppitz <gi...@git.apache.org>.
Github user dkuppitz commented on a diff in the pull request:

    https://github.com/apache/tinkerpop/pull/531#discussion_r95836475
  
    --- Diff: docs/src/recipes/appendix.asciidoc ---
    @@ -0,0 +1,226 @@
    +////
    +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._
    --- End diff --
    
    Another way for undirected edges:
    
    ```
    gremlin> g.V().where(without("x")).as("a").
    ......1>   outE().as("e").inV().as("b").
    ......2>   filter(bothE().where(neq("e")).otherV().where(eq("a"))).store("x").
    ......3>   select("a","b").dedup()
    ==>[a:v[1],b:v[3]]
    ```
    
    @okram I thought the following query should have the same result, but it doesn't (result is empty):
    
    ```
    g.V().where(without("x")).as("a").
      outE().as("e").inV().as("b").
      where(bothE().where(neq("e")).otherV().as("a")).store("x").
      select("a","b").dedup()
    ```
    
    The difference is `filter(...where(eq("a")))` (works) vs. `where(....as("a"))` (doesn't work).


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---