2

I have been working on trying to retrieve different values from different vertices from one starting point in my graph (I have a few other related posts, but I have worked out most of my issues). I am using TinkerPop with Neptune DB.

I am able to successfully query for different values from different nodes in the graph, but my output is odd and I am not sure how to parse it properly. Here is a sample output:

var: {{{{name=[Soccer]}=1}=1, {{studentId=[123456s]}=1, {}=1}=1, {{adminId=[12345], area=[LOCAL]}=1}=1}=1}

I am using this to output it in my Java app:

BulkSet<Object> var = t.cap("primary").toBulkSet();
System.out.println("var: " + var);

The way I am getting the values is (simplified):

  1. From starting point, go to first node that contains values needed. Store those values with .aggregate("a"). In my sample output, it started at the School vertex and then went to the Student vertex and got the values.

  2. Start at starting point again, and go to next node that contains more values needed. Store those values with another .aggregate("b") (with a different list name). In my sample output, it started back at the School vertex and then went to the Admin vertex.

  3. Repeat step 2 until we traverse to all nodes containing the different values we need. In my sample output, it started again at the School vertex, and then went to the Sport vertex.

  4. Collect all stored lists into one "primary" list with .aggregate("primary").

  5. Get results with .cap("primary").toBulkSet

It might not be the best method, but I have gotten it to actually go to each node and get the results I need, so unless I can find a better way to go out to all nodes at once (?)...basically performing steps 3 and 4 all in step 2? But I am unsure how to do that.

Anyways, I am also having some issues setting up an example graph query, but here is a sample graph:

g.addV('Admin').as('1').
  property(single, 'adminId', 12345).
  property(single, 'active', true).
  property(single, 'area', 'LOCAL').
  addV('School').as('2').
  property(single, 'name', 'Jefferson').
  property(single, 'address', '1234 Road Way').
  addV('Class').as('3').
  property(single, 'name', 'Math').
  property(single, 'location', 'UP2').
  property(single, 'level', 2).addV('Req').
    as('4').
  property(single, 'name', 'Math1').
  property(single, 'level', 1).
  property(single, 'completed', true).
  addV('Student').as('5').
  property(single, 'name', 'Matt').
  property(single, 'age', 22).
  property(single, 'address', '2468 Appreciate Way').
  property(single, 'classLevel', 2).
  property(single, 'studentId', '123456s').
  addV('Activity').as('6').
  property(single, 'name', 'ExtraCur').
  property(single, 'groupId', 1422).
  property(single, 'offered', true).
  addV('Sport').as('7').
  property(single, 'name', 'Soccer').
  property(single, 'level', 2).
  addE('administers').from('1').to('2').
  addE('offers').from('2').to('3').
  addE('requirementFor').from('4').to('3').
  addE('attends').from('5').to('2').addE('has').
  from('6').to('7').addE('offers').from('2').
  to('6')

I am pretty much trying to go to different vertices depending on the request fields from a specified starting point.

If I output my traversal, it looks like this:

t: [TinkerGraphStep(vertex,[Void]), FoldStep, TinkerGraphStep(vertex,[{randomized_UUID}]), FoldStep, TinkerGraphStep(vertex,[2]), NoOpBarrierStep(2500), VertexStep(OUT,[offers],vertex), NoOpBarrierStep(2500), VertexStep(OUT,[has],vertex), AggregateGlobalStep(Sport,[PropertyMapStep([name],value)]), TinkerGraphStep(vertex,[{randomized_UUID}]), FoldStep, TinkerGraphStep(vertex,[2]), NoOpBarrierStep(2500), VertexStep(IN,[attends],vertex), AggregateGlobalStep(Student,[PropertyMapStep([studentId],value)]), TinkerGraphStep(vertex,[{randomized_UUID]), FoldStep, TinkerGraphStep(vertex,[2]), NoOpBarrierStep(2500), VertexStep(IN,[administers],vertex), AggregateGlobalStep(Admin,[PropertyMapStep([adminId, area],value)]), AggregateGlobalStep(primary,[SideEffectCapStep([Sport])]), AggregateGlobalStep(primary,[SideEffectCapStep([Student])]), AggregateGlobalStep(primary,[SideEffectCapStep([Admin])]), SideEffectCapStep([primary])]

If I were to write out the actual traversal, it would look like this:

g.V("Void").fold(). // we set this up start the traversal
   V(UUID.randomUUID().toString()).fold().   // random UUID used to prevent accidental `.V().V()`
   V(2).out("offers").out("has").            // traverse to Sport node to get fields
   aggregate("Sport").by(valueMap([name]).   // Store Sport field in list
   V(UUID.randomUUID().toString()).fold().   // go back to start
   V(2).in("attends").                       // traverse to Student node to get fields
   aggregate("Student").by(valueMap([studentId]).  // Store Student fields in new list
   V(UUID.randomUUID().toString()).fold().   // go back to start
   V(2).in("administers").                   // traverse to Admin node to get fields
   aggregate("Admin").by(valueMap([adminId, area]).  // Store Admin fields in new list
   aggregate("primary").by(cap("Sport")).    // start combining all lists into one
   aggregate("primary").by(cap("Student")).  // combine next list into primary
   aggregate("primary").by(cap("Admin")).    // combine last list into primary
   cap("primary").toBulkSet()                // cap primary list and put in BulkSet variable

Anyways, it might not be the cleanest, but I am able to get the field names and values I want. I am just not sure how to parse the result into something simple. When I use var.get("studentId") it returns just 0.

Thanks in advance!

Kelvin Lawrence
  • 14,674
  • 2
  • 16
  • 38
Ebad
  • 131
  • 11
  • I'm not sure I follow what the "Void" and `randomUUID` IDs are being used for. I think you can do this all in one query using either (a) a `path` step with several `by` modulators, or (b) a `choose` step. You should not need to keep doing `V(2)`. A third option is just to use `union`. As time allows I'll try to create an example. – Kelvin Lawrence Sep 22 '22 at 23:19
  • Looking at this some more, is `V(2)` the `School` node? – Kelvin Lawrence Sep 23 '22 at 15:14
  • Yes, it is the School node, and I think I should have written that out as a String. Apologies. – Ebad Sep 23 '22 at 16:11
  • Thanks, I added an answer below that I think simplifies things quite a bit. – Kelvin Lawrence Sep 23 '22 at 20:23

1 Answers1

3

Assuming that V(2) is the "School" node, then using a union you can retrieve the structure as follows. Having got this structure you can of course format the results any way you wish. For this example initially, I just chose to show the labels.

gremlin>    g.V().hasLabel('School').
......1>          union(  out('offers').out('has'),
......2>                __.in('attends'),
......3>                __.in('administers')).
......4>          path().by(label) 
   
==>[School,Activity,Sport]
==>[School,Student]
==>[School,Admin]   

using this basic query structure, we can now easily produce a version of the output used in the question:

gremlin>    g.V().hasLabel('School').
......1>          union(  out('offers').out('has').valueMap('name').by(unfold()),
......2>                __.in('attends').valueMap('studentId').by(unfold()),
......3>                __.in('administers').valueMap('adminId','area').by(unfold())).
......4>          fold() 

==>[[name:Soccer],[studentId:123456s],[area:LOCAL,adminId:12345]]   

If you prefer a fully flattened map, we can do:

gremlin>    g.V().hasLabel('School').
......1>          union(  out('offers').out('has').valueMap('name').by(unfold()),
......2>                __.in('attends').valueMap('studentId').by(unfold()),
......3>                __.in('administers').valueMap('adminId','area').by(unfold())).
......4>          unfold().fold() 
 
==>[name=Soccer,studentId=123456s,area=LOCAL,adminId=12345]   
Kelvin Lawrence
  • 14,674
  • 2
  • 16
  • 38