My use case is that a current user of the system has access to
a set of groups. These groups can belong to
other groups. So I would like to get to all the base groups and the parent groups in one query.
g.V(currentUser)
.out("access-to")
.has(label, "groups")
.as("baseGrps")
.aggregate("baseGrps")
.until(out("belongs-to").count().is(0))
.repeat(out("belongs-to").simplePath().aggregate("grps"))
.cap("grps")
.unfold()
.dedup()
.as("nestedGrps")
.select("baseGrp", "nestedGrps")
However this is returning a map obviously, However I'd like to return just vertices so I can union
in another traversal.
The seed sample data to run the above query against
g.addV("user")
.property("name", "AUser")
.as("aUser")
.addV("group")
.property("name", "UserGroup")
.as("userGroup")
.addV("userGroup")
.property("name", "AdminUserGroup")
.as("adminUserGroup")
.addV("group")
.property("name", "AllUserGroup")
.as("allUserGrp")
.select("aUser")
.addE("access-to")
.to("userGroup")
.select("userGroup")
.addE("belongs-to")
.to("adminUserGroup")
.select("adminUserGroup")
.addE("belongs-to")
.to("allUserGrp")
.select("aUser")
.next();
Any hints or improvements ideas will be really appreciated.
UPDATE: The below query is what seems to work for me. Not sure if its the best way.
g.V().has(T.label, "user").has("name", "AUser")
.union(
out("access-to")
.as("baseGroups")
.until(out("belongs-to").count().is(0))
.repeat(out("belongs-to").simplePath())
.emit()
.dedup()
.as("subGroups"),
out("access-to")
)
Thanks for updating your answer. Note that your sample data script could be written as:
g.addV("user").
property("name", "AUser").as("aUser").
addV("group").property("name", "UserGroup").as("userGroup").
addV("userGroup").property("name", "AdminUserGroup").as("adminUserGroup").
addV("group").property("name", "AllUserGroup").as("allUserGrp").
addE("access-to").from("aUser").to("userGroup").
addE("belongs-to").from("userGroup").to("adminUserGroup").
addE("belongs-to").from("adminUserGroup").to("allUserGrp").
iterate()
which might be more intuitive for some. Also note, that the working traversal you supplied could be simplified a bit to:
gremlin> g.V().has("user","name", "AUser").
......1> union(out("access-to").
......2> until(outE("belongs-to").count().is(0)).
......3> repeat(out("belongs-to").simplePath()).
......4> emit().
......5> dedup(),
......6> out("access-to"))
==>v[4]
==>v[6]
==>v[2]
Note the use of outE()
in until()
as opposed to out()
- that is an important optimization as you only need to count edges there to break out of the loop. Counting adjacent vertices on those edges is more costly.
Here's my attempt at improving upon what you've gotten so far:
gremlin> g.V().has("user","name", "AUser").
......1> repeat(out('access-to','belongs-to').simplePath()).
......2> until(outE("belongs-to").count().is(0)).
......3> emit().
......4> dedup()
==>v[2]
==>v[4]
==>v[6]
Perhaps I'm making too many assumptions given the example data you provided but if you can assume there are no outgoing "access-to" edges from the "group" vertices then Gremlin will only be traversing "belongs-to" edges after the first step away from the "user" and the traversal gives you what you want. I guess you still need the simplePath()
and dedup()
steps as I imagine groups can belong to multiple parent groups at which point you might traverse the same paths over and over again.