I have a graph with two edge definitions like this:
isDepartment: [organisation] -> [organisation]
hasAccess: [user] -> [organisation]
Organisations are nested in a tree (no cycles). There are multiple top-level organisations without any incoming isDepartment
edges.
Users are granted access to one or more organisations. These can be top-level organisations or organisations somewhere lower down the tree. If a user has access to an organisation, it has access to all child organisations.
I am trying to build a custom visitor or filter that gives me all accessible organisations for a user, including it's path to the root, along with a property if they are accessible or not.
Example, take the following organisation structure:
- Root A
- Dept. A.1
- Dept. A.2
- Root B
- Dept. B.1
- Dept. B.2
- Subdept. B.2.1
- Root C
- Dept. C.1
- Dept. C.2
Now take a user that hasAccess to Root A
and Dept. B.2
. I would like to generate the following result tree:
- Root A, accessible: true
- Dept. A.1, accessible: true
- Dept. A.2, accessible: true
- Root B, accessible: false
- Dept. B.2, accessible: true
- Subdept. B.2.1, accessible: true
Note that Root C
and Dept. B1
are not in the result because they are not accessible to the user, nor are any of their children accessible.
Also note that Root B
is included but marked as not accessible
. This is because teh user is granted access to only a child of Root B
but not the root itself.
How can I write a custom function/visitor/filter that accomplished this?
This was indeed a challenging question, thank you very much ;)
You can solve this by adding user-defined functions to AQL and use them in the TRAVERSER.
First of all I have registered two AQL visitor functions through arangosh:
var aqlfunctions = require("org/arangodb/aql/functions");
aqlfunctions.register("myvisitor::indirectAccess", "function (config, result, vertex) { if(result.length === 0) {result.push({});} result[0][vertex._key] = {hasAccess: true};}")
aqlfunctions.register("myvisitor::noAccess", "function (config, result, vertex) { if (result.length === 0) {result.push({});} result[0][vertex._key] = {hasAccess: false};}")
These functions simply do the following:
myvisitor::indirectAccess
will be used to traverse down the tree. As in AQL the result is always an Array, we simply at a first document to it (if necessary) to store all the data. Then we assign to the vertexes_key
property the value{hasAccess: true}
.myvisitor::noAccess
will be used to traverse up the tree und will store '{hasAccess: false}` in the same way.
Now we can execute the following query which makes use of these visitors:
FOR x IN GRAPH_NEIGHBORS(@graph, @userId, {direction: 'outbound'})
LET upwards = TRAVERSAL(organisation, isDepartment, x, 'inbound', {visitor: 'myvisitor::noAccess'})[0]
LET downwards = TRAVERSAL(organisation, isDepartment, x, 'outbound', {visitor: 'myvisitor::indirectAccess'})[0]
RETURN MERGE(upwards, downwards)
Short explaination:
- Find the organizations this user has direct access to.
- Go up the tree
upwards
and mark everything as "noAccess". - Go down the tree
downwards
and mark everything as "access". - Merge
upwards
anddownwards
.
If you would like to modify the result format you have to change the registered visitor functions.
来源:https://stackoverflow.com/questions/33479649/arangodb-custom-filter-visitor-for-my-tree-graph