2016-12-04 32 views
0

我目前正在建模一個數據庫超過50.000個節點,每個節點有2個定向關係。我嘗試獲取一個輸入節點(根節點)的所有節點,這些節點通過一個關係連接到它,並且這些節點的所有子節點都是這樣,直到到達與該根節點直接和間接連接的每個節點。Cypher沒有循環,沒有雙重路徑

String query = 
    "MATCH (m {title:{title},namespaceID:{namespaceID}})-[:categorieLinkTo*..]->(n) " + 
    "RETURN DISTINCT n.title AS Title, n.namespaceID " + 
    "ORDER BY n.title"; 

Result result = db.execute(query, params); 
String infos = result.resultAsString(); 

我已閱讀,運行時更可能發生在爲O(n^x)中,但我不能找到排除例如環或多個路徑在一個節點的任何命令,所以查詢採用在2小時內簡單這對我的用例來說是不可接受的。

+0

Cypher中沒有'GROUP BY'運算符。你的意思是'ORDER BY'? –

+0

嘿,是的,我的不好。這只是一個嘗試,如果這樣的事情在這裏工作..我忘了刪除。它沒有與ORDER BY一起工作。 – DanDo

回答

1

對於簡單的關係表達式,Cypher支架排除多個關係通過強制uniqueness自動:

雖然模式匹配,Neo4j的確保爲不包括其中相同的圖形關係被發現多次在一個單一的圖案匹配。

的文檔不是這是否適用於可變長度的路徑完全清楚 - 讓我們設計一個小實驗確認:

CREATE 
    (n1:Node {name: "n1"}), 
    (n2:Node {name: "n2"}), 
    (n3:Node {name: "n3"}), 
    (n4:Node {name: "n4"}), 
    (n1)-[:REL]->(n2), 
    (n2)-[:REL]->(n3), 
    (n3)-[:REL]->(n2), 
    (n2)-[:REL]->(n4) 

這導致如下圖:

enter image description here

查詢有:

MATCH (n:Node {name:"n1"})-[:REL*..]->(m) 
RETURN m 

結果是:

╒══════════╕ 
│m   │ 
╞══════════╡ 
│{name: n2}│ 
├──────────┤ 
│{name: n3}│ 
├──────────┤ 
│{name: n2}│ 
├──────────┤ 
│{name: n4}│ 
├──────────┤ 
│{name: n4}│ 
└──────────┘ 

正如你可以看到n4包含多次(因爲它可以與避免循環,並通過循環的那麼順利訪問)。 與PROFILE檢查執行:

enter image description here

所以我們應該用DISTINCT擺脫重複的:

MATCH (n:Node {name:"n1"})-[:REL*..]->(m) 
RETURN DISTINCT m 

結果是:

╒══════════╕ 
│m   │ 
╞══════════╡ 
│{name: n2}│ 
├──────────┤ 
│{name: n3}│ 
├──────────┤ 
│{name: n4}│ 
└──────────┘ 

再次檢查用PROFILE執行:

enter image description here

+0

謝謝你的回答。 – DanDo

+0

至少在我的查詢中,當我將深度減少到例如6有多次相同的節點。 – DanDo

+0

當我刪除獨特的有多個相同的節點..所以查詢需要簡單的年齡。 – DanDo

1

確實有一些事情我們可以做,以改善此查詢。

其中之一,你根本沒有使用標籤。由於您沒有使用標籤,因此輸入節點上的匹配無法利用您可能擁有的任何模式索引,並且它必須掃描所有訪問和比較屬性的50k節點,直到找到所有節點給定的標題和命名空間(當發現它時它不會停止,因爲它不知道是否有其他節點滿足條件)。您可以通過單獨匹配開始節點來檢查時間。

爲了改善這一點,您的節點應該被標記,並且您的起始節點上的匹配應該包含標籤,並且您的title和namespaceID屬性應該被編入索引。

僅此一項就可以顯着提高查詢速度。

接下來的問題是,如果剩下的瓶頸更多歸因於排序,或返回一個巨大的結果集?

您可以通過限制返回的結果來檢查單獨排序的成本。

在匹配後,您可以將此用於查詢的結尾。

WITH DISTINCT n 
ORDER BY n.title 
LIMIT 10 
RETURN n.title AS Title, n.namespaceID 
ORDER BY n.title 

同樣,在做任何性能優化的時候,你就應該剖析你的查詢(至少在合理時間內完成的)並解釋說,正在時間的荒謬量檢查查詢的那些計劃。

+0

謝謝你的回答。我使用限制爲10(摩爾在[:categorieLinkTo * .. 10]的方式,但我認爲它是一樣的,沒有獨特的我有超過55.000節點回來。我所有的節點都標籤,但他們都保持相同的標籤(因爲稍後數據庫會變得更大),所以主要的問題是,查詢dosnt意識到,它已經訪問了一些節點。但是我現在正在自己開發一個簡單的BFS,因爲Cypher沒有提供任何有用的 – DanDo

+0

LIMIT 10不同之處在於它只返回總計10個結果,建議是僅僅因爲大量返回的行(返回很多似乎沒有意義)來測試查詢是否低效或者如果還有其他主要的低效率也會導致大規模的放緩,我認爲它存在 – InverseFalcon

+0

如果您打算使用索引來查找您的開始節點,那麼您需要在查詢中添加標籤。現在它正在進行完整的圖形掃描和財產在所有55k節點上比較JUST找到節點來開始查詢的其餘部分。你可以通過查看執行需要多長時間來測試它:'MATCH(m {title:{title},namespaceID:{namespaceID}})'你也可以嘗試剖析它 – InverseFalcon

0
public static HashSet<Node> breadthFirst(String name, int namespace, GraphDatabaseService db) { 

     // Hashmap for storing the cypher query and its parameters 
     Map<String, Object> params = new HashMap<>(); 


     // Adding the title and namespaceID as parameters to the Hashmap 
     params.put("title", name); 
     params.put("namespaceID", namespace); 

     /*it is a simple BFS with these variables below 
     * basically a Queue (touched) as usual, a Set to store the nodes 
     * which have been used (finished), a return variable and 2 result 
     * variables for the queries 
     */ 
     Node startNode = null; 
     String query = "Match (n{title:{title},namespaceID:{namespaceID}})-[:categorieLinkTo]-> (m) RETURN m"; 
     Queue<Node> touched = new LinkedList<Node>(); 
     HashSet<Node>finished = new HashSet<Node>(); 
     HashSet<Node> returnResult = new HashSet<Node>(); 

     Result iniResult = null; 
     Result tempResult=null; 

     /*the part below get the direct nodes and puts them 
     * into the queue 
     */ 
      try (Transaction tx = db.beginTx()) { 
       iniResult =db.execute(query,params); 


       while(iniResult.hasNext()){ 
        Map<String,Object> iniNode=iniResult.next(); 
        startNode=(Node) iniNode.get("m"); 
        touched.add(startNode); 
        finished.add(startNode); 
       } 
       tx.success(); 
       }catch (QueryExecutionException e) { 
       logger.error("Fehler bei Ausführung der Anfrage", e); 
       } 

      /*and now we just execute the BFS (don't think i need more to 
      * say here.. we are all pros ;)) 
      * as usual, marking every node we have visited 
      * and saving every visited node. 
      * the difficult part had been the casting from 
      * and to node and result, everything else is pretty much 
      * straightforward. I think the variables explain their self 
      * via their name.... 
      */ 

       while(! (touched.isEmpty())){ 
       try (Transaction tx = db.beginTx()) { 
        Node currNode=touched.poll(); 
        returnResult.add(currNode); 

        tempResult=null;    
        Map<String, Object> paramsTemp = new HashMap<>(); 
        paramsTemp.put("title",currNode.getProperty("title").toString()); 
        paramsTemp.put("namespaceID", 14); 
        String tempQuery = "MATCH (n{title:{title},namespaceID:{namespaceID}})-[:categorieLinkTo] -> (m) RETURN m"; 
        tempResult = db.execute(tempQuery,paramsTemp); 



       while(tempResult.hasNext()){ 
        Map<String, Object> currResult= null; 
        currResult=tempResult.next(); 
        Node tempCurrNode = (Node) currResult.get("m"); 

        if (!finished.contains(tempCurrNode)){ 
         touched.add(tempCurrNode); 
         finished.add(tempCurrNode); 


        } 


        } 


       tx.success(); 
      }catch (QueryExecutionException f) { 
       logger.error("Fehler bei Ausführung der Anfrage", f); 
      } 
     }  


     return returnResult; 

} 

因爲我無法找到一個合適的Cypher表達式,所以我自己寫了一個漂亮的BFS - 它看起來很有效。