Gremlin - Choose one item at random

2020-01-22 12:09发布

问题:

Consider me as 'user' 1. The aim of the query is to get the posts 'posted' by the people I follow and for each of those posts check:

  1. Whether it has been liked by me
  2. Whether it has been liked by anyone else that I follow and if yes choose one of those users at random to return

Sample data:

g.addV('user').property('id',1).as('1')
  addV('user').property('id',2).as('2').
  addV('user').property('id',3).as('3').
  addV('user').property('id',4).as('4').
  addV('post').property('postId','post1').as('p1').
  addV('post').property('postId','post2').as('p2').
  addE('follow').from('1').to('2').
  addE('follow').from('1').to('3').
  addE('follow').from('1').to('4').
  addE('posted').from('2').to('p1').
  addE('posted').from('2').to('p2').
  addE('liked').from('1').to('p2').
  addE('liked').from('3').to('p2').
  addE('liked').from('4').to('p2').iterate()

Query: (As answered here: Graph/Gremlin for social media use case) g.V().has('id',1).as('me').out('follow').aggregate('followers').out('posted').group().by('postId').by(project('likedBySelf','likedByFollowing').by(__.in('liked').where(eq('me')).count()).by(__.in('liked').where(within('followers')).order().by(shuffle).values('id').fold()))

Output:

[post1:[likedBySelf:0,likedByFollowing:[]],post2:[likedBySelf:1,likedByFollowing:[4,3]]]

This query is able to shuffle the values but shows all of the 'id's, now I want to select only the first 'id'. Using .next() instead of .fold() causes exception java.util.NoSuchElementException Is it possible to choose randomly without having to evaluate all the traversals first and then shuffling them?

Desired Output:

[post1:[likedBySelf:0,likedByFollowing:[]],post2:[likedBySelf:1,likedByFollowing:[3]]]

Or

[post1:[likedBySelf:0,likedByFollowing:[]],post2:[likedBySelf:1,likedByFollowing:[4]]]

回答1:

You're pretty close to your answer:

gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV('user').property('id',1).as('1').
......1>   addV('user').property('id',2).as('2').
......2>   addV('user').property('id',3).as('3').
......3>   addV('user').property('id',4).as('4').
......4>   addV('post').property('postId','post1').as('p1').
......5>   addV('post').property('postId','post2').as('p2').
......6>   addE('follow').from('1').to('2').
......7>   addE('follow').from('1').to('3').
......8>   addE('follow').from('1').to('4').
......9>   addE('posted').from('2').to('p1').
.....10>   addE('posted').from('2').to('p2').
.....11>   addE('liked').from('1').to('p2').
.....12>   addE('liked').from('3').to('p2').
.....13>   addE('liked').from('4').to('p2').iterate()
gremlin> g.V().has('id',1).as('me').
......1>   out('follow').
......2>   aggregate('followers').
......3>   out('posted').
......4>   group().
......5>     by('postId').
......6>     by(project('likedBySelf','likedByFollowing').
......7>          by(__.in('liked').where(eq('me')).count()).
......8>          by(__.in('liked').where(within('followers')).order().by('id',shuffle).values('id').limit(1).fold()))
==>[post2:[likedBySelf:1,likedByFollowing:[3]],post1:[likedBySelf:0,likedByFollowing:[]]]

I pretty much just added limit(1) so that only the first item after shuffle is selected. It took a few executions but I was able to see both of the outputs you were looking for with this method. As I suggested on your other question, you might also use sample(1):

gremlin> g.V().has('id',1).as('me').
......1>   out('follow').
......2>   aggregate('followers').
......3>   out('posted').
......4>   group().
......5>     by('postId').
......6>     by(project('likedBySelf','likedByFollowing').
......7>          by(__.in('liked').where(eq('me')).count()).
......8>          by(__.in('liked').where(within('followers')).sample(1).values('id').fold()))
==>[post2:[likedBySelf:1,likedByFollowing:[3]],post1:[likedBySelf:0,likedByFollowing:[]]]
gremlin> g.V().has('id',1).as('me').
......1>   out('follow').
......2>   aggregate('followers').
......3>   out('posted').
......4>   group().
......5>     by('postId').
......6>     by(project('likedBySelf','likedByFollowing').
......7>          by(__.in('liked').where(eq('me')).count()).
......8>          by(__.in('liked').where(within('followers')).sample(1).values('id').fold()))
==>[post2:[likedBySelf:1,likedByFollowing:[4]],post1:[likedBySelf:0,likedByFollowing:[]]]