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):
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.
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.
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.
Collect all stored lists into one "primary" list with .aggregate("primary")
.
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!
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]