2016-10-06 205 views
1

我有一個包含三個集合的圖,其中的項可以通過邊進行連接。 ItemA是itemB的父項,而itemB又是itemC的父項。 元素只能通過邊緣方向連接Arangodb AQL遞歸圖遍歷

"_from : child, _to : parent" 

目前我只能得到「線性」這個AQL查詢結果:

LET contains = (FOR v IN 1..? INBOUND 'collectionA/itemA' GRAPH 'myGraph' RETURN v) 

    RETURN { 
     "root": { 
      "id": "ItemA", 
      "contains": contains 
     } 
    } 

而結果是這樣的:

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

但我需要得到一個像圖層遍歷的「分層」結果:

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

那麼,我可以得到這個運行aql查詢的「分層」結果嗎?

還有一件事:遍歷應運行,直到遇到葉節點。所以遍歷的深度是未知的。

+0

一些相關的技術可能會幫助你: 'v,e,p in 1..3 inbound'並返回'p'。如果你想要更具體,你可以使用'p.vertices [0],p.vertices [1],p.vertices [2]'。從那裏你可以構造你的返回來顯示你想要的值,儘管'p'已經是分層格式。 –

+0

已知最大嵌套深度了嗎?還是它是遞歸的,沒有可預測的深度? –

+0

爲什麼結果必須是分層的?它是否應該防止結果集中的重複? – CoDEmanX

回答

2

我找到了解決辦法。我們決定使用UDF(user defined functions)。

這裏有幾個步驟來構建適當的分層結構:

  1. 註冊在阿朗戈分貝功能。
  2. 運行您的aql查詢,該查詢構造了一個扁平結構(頂點和該頂點的相應路徑)。並將結果作爲你的UDF函數的輸入參數傳遞。 這裏我的函數的每個元素只是追加到其父

在我的情況: 1)註冊在阿朗戈分貝功能。

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)運行AQL與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) 

注:請不要忘記the naming convention當實現你的功能。

1

我也需要知道這個問題的答案,所以這裏有一個解決方案。

我確定代碼需要爲您定製,並且可以做一些改進,請對此樣例答案進行相應評論。

解決方案是使用支持遞歸和構建樹的Foxx Microservice。我遇到的問題是圍繞循環路徑,但是我實現了一個最大深度限制來阻止這個問題,在下面的例子中硬編碼爲10。

要創建福克斯的microService:

  1. 創建一個新的文件夾(如遞歸樹)
  2. 創建目錄的腳本
  3. 將文件manifest.jsonindex.js到根目錄
  4. 廣場腳本目錄中的文件setup.js
  5. 然後創建一個新的zip文件,其中包含這三個文件(例如Foxx.zip
  6. 導航到ArangoDB管理控制檯
  7. 單擊服務|添加服務
  8. 輸入適當的掛載點,例如, /我的/樹
  9. 點擊郵編選項卡上
  10. 在您創建的Foxx.zip拖動文件,它應該建立一個沒有問題
  11. 如果你得到一個錯誤,保證藏品myItemsmyConnections不存在,並且曲線圖稱爲myGraph不存在,因爲它會嘗試使用示例數據創建它們。
  12. 然後導航到ArangoDB管理控制檯,服務| /我的/樹
  13. 點擊API
  14. 展開/樹/ {rootId}
  15. 提供意達的rootId參數,然後單擊「試一試」
  16. 你應該看到的結果,從提供的根ID 。

如果rootId不存在,它沒有返回 如果rootId沒有孩子,如果rootId擁有循環「包含」的價值觀,它返回嵌套最多它返回「包含」 空數組深度限制,我希望有一個更簡潔的方法來阻止這一點。

這裏有三個文件: setup.js(將位於腳本文件夾):

'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(將設在根文件夾):

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

index.js(將設在根文件夾):

'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); 

現在你可以調用佛xx Microservice API終點,提供rootId它將返回完整的樹。這很快。

本作意達的輸出的例子是:

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

你可以看到,項目B包含兩個孩子,ItemC和ItemD,然後ItemD還含有ItemE。

我不能等到ArangoDB AQL改進了對FOR v, e, p IN 1..100 OUTBOUND 'abc/def' GRAPH 'someGraph'樣式查詢中可變深度路徑的處理。自定義訪問者不推薦在3.x中使用,但並未真正替換爲處理路徑頂點深度上的通配符查詢或在路徑遍歷上處理pruneexclude樣式命令的功能。

如果這可以簡化,很樂意發表評論/反饋。

+0

AQL支持可變深度路徑 - 您可以使用'path.vertices [n]'或'path.edges [n]'來檢索任何深度圖層,其中n是深度。 –

+0

是的,它的確如此,但不幸的是你必須指定n,你不能使用通配符。例如,假設您想要查詢節點的所有出站路徑,1..10深,然後執行特殊操作,如果該路徑中的某條邊具有特定屬性,或路徑中的頂點具有一個值。您最終編寫了指定n次的代碼,您擁有巨大的IF命令。我希望它像'IF path.vertices [*]。myKey =='trigger''一樣簡單,並且它會針對該圖形查詢的每個可能的深度進行動態處理。如果滿足觸發條件,也可以取消處理路徑。 –