0

I have a set of data in my DB (AWS Neptune), and I query it using Gremlin (NodeJS).

enter image description here

The data looks like this and I have a query that looks like this:

g.V().hasLabel('group').inE('belongs_to')                       
     .outV().has('user_id', user_id).outE()                           
       .otherV().inE().hasLabel('shared_with')                     
           .outV().has('type', type).aggregate('object_id').fold() 
     .V().hasLabel('user').has('user_id', user_id).inE().hasLabel('shared_with')
         .outV().has('type', type).aggregate('object_id').cap('object_id')
     .unfold().dedup().valueMap().toList();

What I end up with (which is correct) is a list of objects and whom they are shared with, the "whom" can be a user or a group. So that was a good start (took me a while to get it, and I grabbed the main idea from StackOverflow), but I don't get the properties of the edges.

Initially, I had two separate queries, one for objects shared with groups, and one for objects shared with users. That worked fine, but obviously took more time and I had to re-conciliate the two lists I was getting.

I have tried to modify my query putting as('x') behind the edges and the Vertices that I need, but then the select was returning nothing at all. Without the select(), the as() is ignored, and I would get the same data as before.

I initially tried with a .local(__.union()) but that didn't work either. I started Gremlin/Neptune last week, and while I made decent progress, I can only use somewhat simple queries :(

Update:

Here's how I create groups:

let group = await g.addV('group')
                .property('group_id', group_id)
                .property('name', name)
                .property('type', groupType)
                .property('owner', owner)
                .next();

//group_id and owner are UUID.

How I add a user to a group:

t = await g.addV('user')
          .property('user_id', user.id)
          .property('name', user.name)
          .next();

t = await g.V()
          .has('user', 'user_id', user.id)     // Find the user
          .addE('belongs_to')                  // Add a new edge
          .to(g.V().has('group', 'group_id', group_id))
          
// user.id is a UUID

Object creation:

let obj = await g.addV('object')
                .property('object_id', object_id)
                .property('ref', ref)
                .property('type', type)
                .property('owner', owner)                
                .property('creation_time', d.getTime().toString())
                .next();

Sharing with user or group:

t = await g.V().has('object','object_id', object_id).has('owner', owner)
         .addE('shared_with')                // Add a new edge
         .property('read', canRead)
         .property('edit', canEdit)
         .property('share', canShare)
         .to(g.V().has('user', 'user_id', id))  // Find the user to link to
         .next();

// For group: .to(g.V().has('group', 'group_id', id))
E.T
  • 1,095
  • 1
  • 10
  • 19

1 Answers1

1

I wasn't exactly sure what the data you were trying to return was but I gathered that you are looking to answer the question "What objects does this user have access to and what are the permissions?"

If this is the case then the best place to start your traversal from the user vertex from which you can then union() the objects shared with the group to the objects directly shared to the user. Finally, you can project() from the shared_with edges to get the values of the object as well as the edge properties for the permissions.

g.V().has('User', 'user_id', 'A').
union(
    out('belongs_to').inE('shared_with'),
    inE('shared_with')
).project('object', 'permission').
by(outV().valueMap().with(WithOptions.tokens)).
by(valueMap().with(WithOptions.tokens))

The with(WithOptions.tokens) will include the metadata such as the id and label so if this is not needed then you can remove that from the traversal.

bechbd
  • 6,206
  • 3
  • 28
  • 47
  • Thanks, I will try that. I'm getting confused with all the different ways to making queries :) – E.T Oct 21 '20 at 16:22
  • I tried and while it works fine if an object is shared with a user, as soon as an object is shared with a group (that the user belongs to), Neptune craps out: {"requestId":"c1de62bb-5542-46e1-9415-8bf3024f25d6","code":"InternalFailureException","detailedMessage":"An unexpected error has occurred in Neptune."} – E.T Oct 21 '20 at 17:40
  • Seems like Neptune doesn't like with(WithOptions.tokens) too much... – E.T Oct 21 '20 at 17:51
  • So it turns out that Neptune is having some issues with the with(withOptions.tokens). I did some digging and withOptions.tokens resolves to "~tinkerpop.valueMap.tokens". The ~ means that it's hidden. If I use with(""), it works but I get extra stuff that I don't want, and as soon as I pass "~tinkerpop.valueMap.tokens" Neptune craps out... – E.T Oct 21 '20 at 19:17
  • I tested this out on Neptune with the graph you had created above and had no problems with returning objects that are associated with the Group or a user. If you could add the create script that you are using it would be helpful in debugging this issue – bechbd Oct 21 '20 at 20:31
  • Neptune has no issues using withOptions. You are most missing thme import `const withOptions = gremlin.process.withOptions;` to bring the namespace into your node class – bechbd Oct 21 '20 at 20:33
  • I will update the question shortly, I must have done something wrong indeed. – E.T Oct 21 '20 at 22:10