gordyr
gordyr

Reputation: 6276

How can I insert a vertex between two currently connected vertexes?

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:

  1. Create the new post vertex, as above.
  2. Remove the old posted edge between the user and any currently connected post vertex. But only if, a post currently exists.
  3. Attach the new post vertex to the user with a new posted edge.
  4. Then finally, if there was a previous 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

Answers (2)

Daniel Kuppitz
Daniel Kuppitz

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

stephen mallette
stephen mallette

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

Related Questions