Search code examples
gremlintinkerpoptinkerpop3amazon-neptune

Order by vertex property and edge property if it exists


Consider a social media site where a user (vertex) can share (edge) the post (vertex) of another user.

When you construct the users feed, it would look something like this:

const myUserId = 123;

g.V(myUserId)
  .as("myUser")
  .union(
    // posts of users the requesting user follows
    __.out("follow").out("post"),
    // posts shared by users the requesting user follows
    __.out("follow").out("share"),
    // posts of the requesting user
    __.out("post")
  )

So far so good, but I need to sort these posts with the following logic:

  • If the post has an incoming share edge from a user I follow, sort by the createdDate property of that share edge (sort by when the post was shared, not the date the post was created).

  • Otherwise, sort by the createdDate property of the post.

Something like:

.order()
.by(
  __.inE("share")
  .as("shareEdge")
  .outV()
  .in_("follow")
  .hasId(myUserId)
  .select("shareEdge")
  .properties("createdDate"),
  order.decr
    )
.by("createdDate", order.decr)

The problem is that not all posts have a share edge, so this throws an error.

Error: GraphQL error: Error: Error: Server error: {"requestId":"53e7e66c-302e-4521-ac39-b672b4cb52e6","code":"InvalidParameterException","detailedMessage":"The provided traverser does not map to a value: v[c9b66e21-f8fc-48e5-bdcd-c580248b3f52]->[VertexStep(IN,[share],edge)@[shareEdge], EdgeVertexStep(OUT), VertexStep(IN,[follow],vertex), NeptuneHasStep([~id.eq(f70a5a2d-f606-44b8-8fa6-1e359033223e)]), SelectOneStep(last,shareEdge), NoOpBarrierStep(2500), PropertiesStep([createdDate],property)]"} (499)

How can I sort by an edge property only if the edge exists, and fall back to a vertex property sort where it doesn't?


Solution

  • I think that you can solve this with coalesce(). Here's a small sample graph (when asking questions about Gremlin it is always best to include some sample data as a Gremlin script):

    g.addV('post').property('createdDate',1).as('p1').
      addV('post').property('createdDate',2).as('p2').
      addV('user').as('u1').
      addE('share').to('p2').property('createdDate',0)
    

    First the basic ordering using the vertex createdDate:

    gremlin> g.V().hasLabel('post').
    ......1>   order().
    ......2>     by('createdDate').
    ......3>   valueMap(true)
    ==>[id:0,label:post,createdDate:[1]]
    ==>[id:2,label:post,createdDate:[2]]
    

    Then, using coalesce() in the order() to include the logic you described:

    gremlin> g.V().hasLabel('post').
    ......1>   order().
    ......2>     by(coalesce(inE('share').values('createdDate'),
    ......3>                 values('createdDate'))).
    ......4>   valueMap(true) 
    ==>[id:2,label:post,createdDate:[2]]
    ==>[id:0,label:post,createdDate:[1]]