How to add multiple edges from one vertex in a single traversal - gremlin

I have three user vertices. Tom and Mike follow Bob.
g.addV("user").property(single,"name", "bob")
g.addV("user").property(single,"name", "tom")
g.addV("user").property(single,"name", "mike")
g.V().hasLabel("user").has("name", "tom").as("tom").V().hasLabel("user").has("name", "bob").addE("follow").from("tom")
g.V().hasLabel("user").has("name", "mike").as("mike").V().hasLabel("user").has("name", "bob").addE("follow").from("mike")
I'd like to add an alert with edges to the users. There should be an edge from Bob to the alert, and the alert should have edges out to the users that follow Bob.
g.V()
.hasLabel("user")
.has("name", "bob")
.as("bob")
.in("follow")
.as("followers")
.addV("alert")
.as("alert")
.addE("alert")
.from("alert")
.to("followers")
.addE("alert")
.from("bob")
.to("alert")
What I end up with is two alert vertices. Bob has an edge to each of these new alert vertices, and the alerts have edges out to Tom and Mike (the followers).
I believe this is because when I select the followers, it branches (?) the traversal and starts creating the duplicates but this is not what I want. How do I construct this traversal to make the appropriate selections but only create a single alert vertex? What I'm trying to get is this:
bob
|
v
alert
| |
v v
mike tom

You are correct that the duplicates come from the fact that in('follow') produces two traversers and thus addV('alert') gets called multiple times. There are many way you might resolve this, but here is the approach that came immediately to mind for me:
gremlin> g.V().
......1> hasLabel("user").
......2> has("name", "bob").as("b").
......3> addV("alert").as('a').
......4> addE("alerted").from("b").
......5> select('b').
......6> in("follow").
......7> addE("alerted").from("a").iterate()
gremlin> g.V()
==>v[0]
==>v[2]
==>v[4]
==>v[8]
gremlin> g.V().elementMap()
==>[id:0,label:user,name:bob]
==>[id:2,label:user,name:tom]
==>[id:4,label:user,name:mike]
==>[id:8,label:alert]
gremlin> g.E().elementMap()
==>[id:6,label:follow,IN:[id:0,label:user],OUT:[id:2,label:user]]
==>[id:7,label:follow,IN:[id:0,label:user],OUT:[id:4,label:user]]
==>[id:9,label:alerted,IN:[id:8,label:alert],OUT:[id:0,label:user]]
==>[id:10,label:alerted,IN:[id:2,label:user],OUT:[id:8,label:alert]]
==>[id:11,label:alerted,IN:[id:4,label:user],OUT:[id:8,label:alert]]

Related

Using a back step to get two connected vertexes

If I have 3 vertex's A, B, C, where B has an edge to A and C. Starting with B how can I get values for A and C
g.V("b").out("toC").as("c").out("toA").as("a").select("c", "a").next()
This is what I have but it causes an error because I don't think you can go out to A from C since they aren't connected. I need a way to go back to B first but there is no back step that I have seen.
Using this graph
gremlin> g.addV('A').as('a').
......1> addV('B').as('b').
......2> addV('C').as('c').
......3> addE('toA').from('b').to('a').
......4> addE('toC').from('b').to('c')
==>e[42783][42780-toC->42781]
You can find the vertices connected to B using
gremlin> g.V().hasLabel('B').out().elementMap()
==>[id:42774,label:A]
==>[id:42776,label:C]
You can also filter using specific edge labels in cases where there are lots of edges from B and you only want specific ones:
gremlin> g.V().hasLabel('B').out('toA','toC').elementMap()
==>[id:42774,label:A]
==>[id:42776,label:C]
If you really do need to write the query so that it works the way you showed in the question, then this is one way:
gremlin> g.V().hasLabel('B').as('b').
......1> out('toA').as('a').
......2> select('b').
......3> out('toC').as('c').
......4> select('a','c').
......5> by(elementMap())
==>[a:[id:42779,label:A],c:[id:42781,label:C]]
You can also try:
gremlin> g.V().hasLabel('B').
......1> outE().hasLabel('toA','toC').
......2> inV().elementMap()
==>[id:0,label:A]
==>[id:2,label:C]

Gremlin - if multiple vertices return multiple values each, how to limit the result to one per vertex

Essentially, I'm trying to modify the following piece of Gremlin code such that instead of operating on a single vertex at a time - signified by g.V(1), it will work with multiple vertices at once (e.g. changing to g.V()), while still only limiting the number of returned results per vertex to one (see limit(1)).
g.V(1).repeat(out().simplePath()).until(has('color', 'red')).path().limit(1)
The above query will compute the shortest path from a given vertex to the closest vertex which has property(color)==red.
However, I want to compute the shortest path for multiple vertices passed in at the same time, while still only returning a single path per vertex.
I'm having difficulty modifying this without returning multiple paths for the same vertex however.
Deduplicating the result by the start vertex should give you the expected result.
g.V().as('a').
repeat(out().simplePath()).
until(has('color', 'red')).
dedup('a').
path()
Example using the modern toy graph:
gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().
......1> repeat(out().simplePath()).
......2> until(hasLabel('software')).
......3> path()
==>[v[1],v[3]]
==>[v[1],v[4],v[5]]
==>[v[1],v[4],v[3]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
gremlin> g.V().as('a').
......1> repeat(out().simplePath()).
......2> until(hasLabel('software')).
......3> dedup('a').path()
==>[v[1],v[3]]
==>[v[4],v[5]]
==>[v[6],v[3]]

Gremlin: how to traverse backwards across the same edge

I have a simple graph with two vertices, having ids 'a' and 'b'.
I have assigned an edge from 'a' to 'b' with label = 'foo'
gremlin> g.V()
==>v[b]
==>v[a]
gremlin> g.E()
==>e[04b4b9fd-2f20-751d-5673-5aa9d7ce0285][a-foo->b]
My question: how do I traverse backwards along the same edge? For example, if a query traverses to an outbound vertex, how can that query then traverse back across the same edge to the inbound vertex?
My query is shown below:
g.E('04b4b9fd-2f20-751d-5673-5aa9d7ce0285').outV().as('outV')...[want to get the inV for the same edge, here]
There's a lot of different ways to do this. Here's a few that will hopefully inspire you to your answer. You probably shouldn't count on the order in which the vertices are returned in the following case, but you could do bothV():
gremlin> g.E(11).bothV()
==>v[4]
==>v[3]
To force order you could do a union():
gremlin> g.E(11).union(inV(),outV())
==>v[3]
==>v[4]
You could always project() your results:
gremlin> g.E(11).project('in','out').by(inV()).by(outV())
==>[in:v[3],out:v[4]]
If you need to do something with the outV() first and then come back to the edge after that to then traverse on inV() you could label the E() step with as():
gremlin> g.E(11).as('e').outV().hasLabel('person').select('e').inV()
==>v[3]
Hopefully, those examples help.

Gremlin query: how to add multiple edges that contain aggregated information?

My graph contains some "Person" nodes that "ContributedTo" some "Conversations" nodes. I want to write a Gremlin query that will create "TalksWith" edges directly between "Person" nodes. That edge should contain a property "countConversations" that shows how many conversations both these persons contributed to.
Is this possible doing using one Gremlin query for all "Person" nodes at once?
Here's my graph setup (using Gremlin console):
g = TinkerGraph.open().traversal()
g.addV("Person").as("p1").
addV("Person").as("p2").
addV("Person").as("p3").
addV("Person").as("p4").
addV("Person").as("p5").
addV("Conversation").as("c1").
addV("Conversation").as("c2").
addV("Conversation").as("c3").
addE("ContributedTo").from("p1").to("c1").
addE("ContributedTo").from("p2").to("c1").
addE("ContributedTo").from("p3").to("c1").
addE("ContributedTo").from("p1").to("c2").
addE("ContributedTo").from("p2").to("c2").
addE("ContributedTo").from("p3").to("c2").
addE("ContributedTo").from("p4").to("c2").
addE("ContributedTo").from("p5").to("c2").
addE("ContributedTo").from("p1").to("c3").
addE("ContributedTo").from("p3").to("c2")
What I want doing is creating "TalkedWith" edges like this
addE("TalkedWith").from("p1").to("p2").property("countConversations",2)
I wrote a query to count how many conversations a specific person had with other persons
g.V(0L).out("ContributedTo").in("ContributedTo")
.hasId(without(0L)).groupCount().order(local).by(values,desc).next()
Now I want to run this calculation for each person and create "TalksWith" edges.
Here's one way to do it:
gremlin> g.V(0L).hasLabel('Person').
......1> store('p').
......2> out('ContributedTo').
......3> in('ContributedTo').
......4> where(without('p')).
......5> groupCount().
......6> unfold().
......7> addE('TalkedWith').from(select('p').unfold()).to(select(keys)).
......8> property('countConversations',select(values))
==>e[18][0-TalkedWith->1]
==>e[19][0-TalkedWith->2]
==>e[20][0-TalkedWith->3]
==>e[21][0-TalkedWith->4]
gremlin> g.E().hasLabel('TalkedWith').valueMap()
==>[countConversations:2]
==>[countConversations:3]
==>[countConversations:1]
==>[countConversations:1]
Given what you provided in your question as your progress in writing this traversal I'll assume you follow everything up to the groupCount() at line 5. At that point, we have a Map of the people v[0] talked to and the number of times they spoke. The next line deconstructs that Map into its component entries and iterates them creating an edge for each with addE(). The from vertex is gathered from "p" where it was original stored in a List and the to is extracted from the current key in the count map. The "countConversations" property then gets its value from current value of the count map.

How to create a bidirectional edge between two vertices using gremlin?

What is the best way to create a bidirectional edge between two vertices using gremlin. Is there a direct command which adds the edge or should we add two edges like vertex X -> Vertex Y and vertex Y -> Vertex X?
You can add an edge between two vertices and then ignore the direction at query-time by using the both() step. This is how you typically address bidirectional edges in Gremlin.
Let's open the Gremlin Console and create a simple graph where Alice and Bob are friends:
\,,,/
(o o)
-----oOOo-(3)-oOOo-----
gremlin> graph = TinkerGraph.open()
gremlin> g = graph.traversal(standard())
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin>
==>null
gremlin> g.addV(label, 'user', 'name', 'Alice').as('alice').addV(label, 'user', 'name', 'Bob').as('bob').addE('friendWith').from('alice').to('bob')
==>e[4][0-friendWith->2]
This creates a graph with two vertices and one edge:
gremlin> g.V()
==>v[0]
==>v[2]
gremlin> g.E()
==>e[4][0-friendWith->2]
Notice how you cannot traverse from the Bob vertex to the Alice vertex in the outgoing direction, but you can traverse in the ingoing direction (first query yields no result).
gremlin> g.V().has('name', 'Bob').out('friendWith')
gremlin> g.V().has('name', 'Bob').in('friendWith')
==>v[0]
Or starting from Alice (second query yields no result), you get the opposite:
gremlin> g.V().has('name', 'Alice').out('friendWith')
==>v[2]
gremlin> g.V().has('name', 'Alice').in('friendWith')
However, you can traverse the graph in both directions with the both() step, and retrieve Alice's friend or Bob's friend.
gremlin> g.V().has('name', 'Alice').both('friendWith')
==>v[2]
gremlin> g.V().has('name', 'Bob').both('friendWith')
==>v[0]
This would also work on more complex graphs with more than two vertices and one friendship relationship. The both() step simply ignores the direction of the edges when attempting to traverse to adjacent vertices.

Resources