Arangodb custom filter/visitor for my tree graph

2019-05-14 14:17发布

问题:

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?

回答1:

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:

  1. Find the organizations this user has direct access to.
  2. Go up the tree upwards and mark everything as "noAccess".
  3. Go down the tree downwards and mark everything as "access".
  4. Merge upwards and downwards.

If you would like to modify the result format you have to change the registered visitor functions.