| //// |
| 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. |
| //// |
| [[between-vertices]] |
| == Between Vertices |
| |
| It is quite common to have a situation where there are two particular vertices of a graph and a need to execute some |
| traversal on the paths found between them. Consider the following examples using the modern toy graph: |
| |
| [gremlin-groovy,modern] |
| ---- |
| g.V(1).bothE() <1> |
| g.V(1).bothE().where(otherV().hasId(2)) <2> |
| v1 = g.V(1).next();[] |
| v2 = g.V(2).next();[] |
| g.V(v1).bothE().where(otherV().is(v2)) <3> |
| g.V(v1).outE().where(inV().is(v2)) <4> |
| g.V(1).outE().where(inV().has(id, within(2,3))) <5> |
| g.V(1).out().where(__.in().hasId(6)) <6> |
| ---- |
| |
| <1> There are three edges from the vertex with the identifier of "1". |
| <2> Filter those three edges using the `where()`-step using the identifier of the vertex returned by `otherV()` to |
| ensure it matches on the vertex of concern, which is the one with an identifier of "2". |
| <3> Note that the same traversal will work if there are actual `Vertex` instances rather than just vertex identifiers. |
| <4> The vertex with identifier "1" has all outgoing edges, so it would also be acceptable to use the directional steps |
| of `outE()` and `inV()` since the schema allows it. |
| <5> There is also no problem with filtering the terminating side of the traversal on multiple vertices, in this case, |
| vertices with identifiers "2" and "3". |
| <6> There's no reason why the same pattern of exclusion used for edges with `where()` can't work for a vertex between |
| two vertices. |
| |
| The basic pattern of using `where()`-step to find the "other" known vertex can be applied in far more complex |
| scenarios. For one such example, consider the following traversal that finds all the paths between a group of defined |
| vertices: |
| |
| [gremlin-groovy,modern] |
| ---- |
| ids = [2,4,6].toArray() |
| g.V(ids).as("a"). |
| repeat(bothE().otherV().simplePath()).times(5).emit(hasId(within(ids))).as("b"). |
| filter(select(last,"a","b").by(id).where("a", lt("b"))). |
| path().by().by(label) |
| ---- |
| |
| For another example, consider the following schema: |
| |
| image:recipe-job-schema.png[width=750] |
| |
| Assume that the goal is to find information about a known job and a known person. Specifically, the idea would be |
| to extract the known job, the company that created the job, the date it was created by the company and whether or not |
| the known person completed an application. |
| |
| [gremlin-groovy] |
| ---- |
| g.addV("person").property("name", "bob").as("bob"). |
| addV("person").property("name", "stephen").as("stephen"). |
| addV("company").property("name", "Blueprints, Inc").as("blueprints"). |
| addV("company").property("name", "Rexster, LLC").as("rexster"). |
| addV("job").property("name", "job1").as("blueprintsJob1"). |
| addV("job").property("name", "job2").as("blueprintsJob2"). |
| addV("job").property("name", "job3").as("blueprintsJob3"). |
| addV("job").property("name", "job4").as("rexsterJob1"). |
| addV("application").property("name", "application1").as("appBob1"). |
| addV("application").property("name", "application2").as("appBob2"). |
| addV("application").property("name", "application3").as("appStephen1"). |
| addV("application").property("name", "application4").as("appStephen2"). |
| addE("completes").from("bob").to("appBob1"). |
| addE("completes").from("bob").to("appBob2"). |
| addE("completes").from("stephen").to("appStephen1"). |
| addE("completes").from("stephen").to("appStephen2"). |
| addE("appliesTo").from("appBob1").to("blueprintsJob1"). |
| addE("appliesTo").from("appBob2").to("blueprintsJob2"). |
| addE("appliesTo").from("appStephen1").to("rexsterJob1"). |
| addE("appliesTo").from("appStephen2").to("blueprintsJob3"). |
| addE("created").from("blueprints").to("blueprintsJob1").property("creationDate", "12/20/2015"). |
| addE("created").from("blueprints").to("blueprintsJob2").property("creationDate", "12/15/2015"). |
| addE("created").from("blueprints").to("blueprintsJob3").property("creationDate", "12/16/2015"). |
| addE("created").from("rexster").to("rexsterJob1").property("creationDate", "12/18/2015").iterate() |
| vBlueprintsJob1 = g.V().has("job", "name", "job1").next() |
| vRexsterJob1 = g.V().has("job", "name", "job4").next() |
| vStephen = g.V().has("person", "name", "stephen").next() |
| vBob = g.V().has("person", "name", "bob").next() |
| g.V(vRexsterJob1).as('job'). |
| inE('created').as('created'). |
| outV().as('company'). |
| select('job'). |
| coalesce(__.in('appliesTo').where(__.in('completes').is(vStephen)), |
| constant(false)).as('application'). |
| select('job', 'company', 'created', 'application'). |
| by().by().by('creationDate').by() |
| g.V(vRexsterJob1, vBlueprintsJob1).as('job'). |
| inE('created').as('created'). |
| outV().as('company'). |
| select('job'). |
| coalesce(__.in('appliesTo').where(__.in('completes').is(vBob)), |
| constant(false)).as('application'). |
| select('job', 'company', 'created', 'application'). |
| by().by().by('creationDate').by() |
| ---- |
| |
| While the traversals above are more complex, the pattern for finding "things" between two vertices is largely the same. |
| Note the use of the `where()`-step to terminate the traversers for a specific user. It is embedded in a `coalesce()` |
| step to handle situations where the specified user did not complete an application for the specified job and will |
| return `false` in those cases. |