Reputation: 6276
I am trying to to grips with Gremlin. Having thoroughly read the documentation I still appear to be struggling with it conceptually.
I am creating a basic newsfeed, following the model found in the Neo4j documentation here:
http://neo4j.com/docs/snapshot/cypher-cookbook-newsfeed.html
I am actually using titandb, but am following the same sort of principles/schema as shown above.
So far I have created a graph of user
vertexes, which are connected via friend
edges.
And I am able to add a new post
vertex and connect it via a posted
edge to a user
vertex like so:
def activity = graph.addVertex(T.label, "post");
activity.property("post_id", post_id);
activity.property("time", time);
activity.property("body", body);
def g = graph.traversal();
def user = g.V().hasLabel("user").has("userid", userid).next();
user.addEdge("posted", activity, "time", time);
However, I need to be able to do the following in a single Gremlin script:
post
vertex, as above.posted
edge between the user
and any currently connected post
vertex. But only if, a post currently exists.post
vertex to the user
with a new posted
edge.post
vertex, attach it to the newly added post
vertex via a next
edge. Ultimately creating long streams of posts for each user.I've been playing around, using trial and error, for what seems like hours now, and just cannot seem to get my head around it.
Any help would be greatly appreciated.
Upvotes: 2
Views: 141
Reputation: 10904
Another way (using a single traversal):
Create the initial graph with a single user:
gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV("user").property("userid", 123)
==>v[0]
Add the first post:
gremlin> g.V().has("user", "userid", 123).as("user"). /* find the user */
addV("post").as("p").property("post_id", 1). /* add a new post */
property("time", System.currentTimeMillis()).
property("body", "bla bla").
addE("posted").from("user").as("e"). /* connect user and post */
property("time", System.currentTimeMillis()).
outV(). /* traverse to user */
outE("posted").where(neq("e")).as("o"). /* traverse to any pre-existing posted edge */
inV(). /* traverse to pre-existing post */
addE("next").to("p"). /* connect it with the new post */
select("o").drop() /* drop the old posted edge */
gremlin> // check
gremlin> g.V().not(inE()).repeat(union(outE("posted").inV(), inE("next").outV())).until(__.not(union(outE("posted"), inE("next")))).path().by(label)
==>[user, posted, post]
Add another post (same query):
gremlin> g.V().has("user", "userid", 123).as("user").
addV("post").as("p").property("post_id", 1).
property("time", System.currentTimeMillis()).
property("body", "bla bla").
addE("posted").from("user").as("e").
property("time", System.currentTimeMillis()).
outV().
outE("posted").where(neq("e")).as("o").
inV().
addE("next").to("p").
select("o").drop()
gremlin> // check
gremlin> g.V().not(inE()).repeat(union(outE("posted").inV(), inE("next").outV())).until(__.not(union(outE("posted"), inE("next")))).path().by(label)
==>[user, posted, post, next, post]
Upvotes: 5
Reputation: 46226
Here's one way you might do it:
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> createPost = { user, post ->
......1> u = g.V().has('userId', user).next()
......2> previous = g.V(u).outE('posted').inV().tryNext().orElse(null)
......3> g.V(u).outE('posted').drop().iterate()
......4> activity = graph.addVertex(T.label, "post", "postId", post)
......5> u.addEdge("posted", activity)
......6>
......6> if (null != previous) previous.addEdge('next', activity)
......7>
......7> // with titan you would want to commit the transaction
......8> // graph.tx().commit()
......9> }
==>groovysh_evaluate$_run_closure1@522b2631
Then add a user and call the createPost()
function a few times:
gremlin> user = g.addV(label,"user", "userId", "me").next()
==>v[0]
gremlin> createPost("me",1)
gremlin> createPost("me",2)
==>e[8][2-next->5]
gremlin> createPost("me",3)
==>e[12][5-next->9]
gremlin> createPost("me",4)
==>e[16][9-next->13]
gremlin> createPost("me",5)
==>e[20][13-next->17]
You can see the "posted" edge aimed at the newest "post" vertex:
gremlin> g.V().has('userId','me').outE()
==>e[19][0-posted->17]
If we traverse out from there along the incoming "next" edge you can get to the previous post:
gremlin> g.V().has('userId','me').out('posted').in('next')
==>v[13]
The following shows three previous posts:
gremlin> g.V().has('userId','me').out('posted').in('next').in('next')
==>v[9]
You can also traverse to arbitrary depth along next
:
gremlin> g.V().has('userId','me').
out('posted').
repeat(__.in('next')).
until(inE().count().is(0)).emit().
path().
by(choose(label().is('post'),
values('postId'),
values('userId')))
==>[me,5,4]
==>[me,5,4,3]
==>[me,5,4,3,2]
==>[me,5,4,3,2,1]
Upvotes: 4