Arangodb AQL recursive graphics move

I have a graph with three collections whose elements can be connected by edges. ItemA is the parent of item B, which in turn is the parent of itemC. Elements can only be connected by edges in the direction

"_from : child, _to : parent" 

Currently, I can only get a "linear" result using this AQL query:

 LET contains = (FOR v IN 1..? INBOUND 'collectionA/itemA' GRAPH 'myGraph' RETURN v) RETURN { "root": { "id": "ItemA", "contains": contains } } 

And the result is as follows:

 "root": { "id": "itemA", "contains": [ { "id": "itemB" }, { "id": "itemC" } ] } 

But I need to get the "hierarchical" result of the graph traversal as follows:

 "root": { "id": "itemA", "contains": [ { "id": "itemB", "contains": [ { "id": "itemC" } } ] } 

So, can I get this "hierarchical" result executing an aql query?

One more thing: a crawl must be performed until leaf nodes are detected. Thus, the bypass depth is not known in advance.

+1
source share
2 answers

I have found a solution. We decided to use UDF ( user-defined functions ).

Here are a few steps to build the right hierarchical structure:

  • Register the function in arango db.
  • Run an aql query that builds a flat structure (vertex and the corresponding path for that vertex). And pass the result as input to your UDF function. Here, my function simply adds each element to the parent

In my case: 1) Register the function in arango db.

 db.createFunction( 'GO::LOCATED_IN::APPENT_CHILD_STRUCTURE', String(function (root, flatStructure) { if (root && root.id) { var elsById = {}; elsById[root.id] = root; flatStructure.forEach(function (element) { elsById[element.id] = element; var parentElId = element.path[element.path.length - 2]; var parentEl = elsById[parentElId]; if (!parentEl.contains) parentEl.contains = new Array(); parentEl.contains.push(element); delete element.path; }); } return root; }) ); 

2) Run AQL with udf:

  LET flatStructure = (FOR v,e,p IN 1..? INBOUND 'collectionA/itemA' GRAPH 'myGraph' LET childPath = (FOR pv IN p.vertices RETURN pv.id_source) RETURN MERGE(v, childPath)) LET root = {"id": "ItemA"} RETURN GO::LOCATED_IN::APPENT_CHILD_STRUCTURE(root, flatStructure) 

Note: Do not forget the naming convention when implementing your functions.

+2
source

I also needed to know the answer to this question, so here is a solution that works.

I am sure that the code should be customized for you and can make some improvements, please comment accordingly if this is suitable to answer this sample.

The solution is to use Foxx Microservice, which supports recursion and creates a tree. The problem I have is related to cycle loops, but I have fulfilled the maximum depth limit that stops this, hardcoded to 10 in the example below.

To create a Microservice Foxx:

  • Create a new folder (e.g. a recursive tree)
  • Creating Directory Scripts
  • Place the manifest.json and index.js in the root directory
  • Place the setup.js file in the script directory
  • Then create a new zip file with these three files in it (e.g. Foxx.zip )
  • Go to the ArangoDB admin console
  • Click "Service" | Add service
  • Enter the appropriate mount point, e.g. / My / tree
  • Click the Zip tab
  • Drag and drop the Foxx.zip file that you created, it should create without problems.
  • If you receive an error message, make sure that the myItems and myConnections collections myItems not exist, and the myGraph graph myGraph not exist, since it will try to create them with sample data.
  • Then go to the ArangoDB admin console, Services | / My / tree
  • Click API
  • Expand / tree / {rootId}
  • Specify the ItemI rootId parameter and click "Try"
  • You should see the result from the root id provided.

If the rootId does not exist, it returns nothing. If the rootId has no children, it returns an empty array for 'contains'. If the rootId is โ€œcontainsโ€, it returns nesting to the limit of depth, I want a cleaner way to stop this.

Here are three files: setup.js (located in the script folder):

 'use strict'; const db = require('@arangodb').db; const graph_module = require("org/arangodb/general-graph"); const itemCollectionName = 'myItems'; const edgeCollectionName = 'myConnections'; const graphName = 'myGraph'; if (!db._collection(itemCollectionName)) { const itemCollection = db._createDocumentCollection(itemCollectionName); itemCollection.save({_key: "ItemA" }); itemCollection.save({_key: "ItemB" }); itemCollection.save({_key: "ItemC" }); itemCollection.save({_key: "ItemD" }); itemCollection.save({_key: "ItemE" }); if (!db._collection(edgeCollectionName)) { const edgeCollection = db._createEdgeCollection(edgeCollectionName); edgeCollection.save({_from: itemCollectionName + '/ItemA', _to: itemCollectionName + '/ItemB'}); edgeCollection.save({_from: itemCollectionName + '/ItemB', _to: itemCollectionName + '/ItemC'}); edgeCollection.save({_from: itemCollectionName + '/ItemB', _to: itemCollectionName + '/ItemD'}); edgeCollection.save({_from: itemCollectionName + '/ItemD', _to: itemCollectionName + '/ItemE'}); } const graphDefinition = [ { "collection": edgeCollectionName, "from":[itemCollectionName], "to":[itemCollectionName] } ]; const graph = graph_module._create(graphName, graphDefinition); } 

mainfest.json (located in the root folder):

 { "engines": { "arangodb": "^3.0.0" }, "main": "index.js", "scripts": { "setup": "scripts/setup.js" } } 

index.js (located in the root folder):

 'use strict'; const createRouter = require('@arangodb/foxx/router'); const router = createRouter(); const joi = require('joi'); const db = require('@arangodb').db; const aql = require('@arangodb').aql; const recursionQuery = function(itemId, tree, depth) { const result = db._query(aql` FOR d IN myItems FILTER d._id == ${itemId} LET contains = ( FOR c IN 1..1 OUTBOUND ${itemId} GRAPH 'myGraph' RETURN { "_id": c._id } ) RETURN MERGE({"_id": d._id}, {"contains": contains}) `); tree = result._documents[0]; if (depth < 10) { if ((result._documents[0]) && (result._documents[0].contains) && (result._documents[0].contains.length > 0)) { for (var i = 0; i < result._documents[0].contains.length; i++) { tree.contains[i] = recursionQuery(result._documents[0].contains[i]._id, tree.contains[i], depth + 1); } } } return tree; } router.get('/tree/:rootId', function(req, res) { let myResult = recursionQuery('myItems/' + req.pathParams.rootId, {}, 0); res.send(myResult); }) .response(joi.object().required(), 'Tree of child nodes.') .summary('Tree of child nodes') .description('Tree of child nodes underneath the provided node.'); module.context.use(router); 

Now you can call the Foxx Microservice API endpoint, indicating that rootId will return the full tree. It's very fast.

An example of the output of this parameter for ItemA:

 { "_id": "myItems/ItemA", "contains": [ { "_id": "myItems/ItemB", "contains": [ { "_id": "myItems/ItemC", "contains": [] }, { "_id": "myItems/ItemD", "contains": [ { "_id": "myItems/ItemE", "contains": [] } ] } ] } ] } 

You can see that item B contains two child items ItemC and ItemD, and then ItemD also contains ItemE.

I canโ€™t wait for ArangoDB AQL to improve the processing of variable depth paths in FOR v, e, p IN 1..100 OUTBOUND 'abc/def' GRAPH 'someGraph' style queries FOR v, e, p IN 1..100 OUTBOUND 'abc/def' GRAPH 'someGraph' . User visitors were not recommended for use in 3.x, but in fact they were not replaced by something powerful for processing wild card requests at the top of the path or processing prune or exclude style commands on the VTP path.

I would like to have comments / feedback if this can be simplified.

+1
source

All Articles