2014-07-21 55 views
2

我在玩Neo4j。我有一個擁有大約400,000個節點的數據庫。我想從CSV文件中插入關係。有大約140萬的關係。如何批量插入關係

我目前使用的是REST API。 REST請求看起來像這個例子:

POST http://localhost:7474/db/data/cypher 
Accept: application/json; charset=UTF-8 
Content-Type: application/json 
{"query": "MATCH (a { ConceptId: '280844000' }), (b { ConceptId: '71737002' }) CREATE (a)-[:Is_a]->(b) RETURN a"} 

問題是每個請求都需要幾秒鐘。這對於我希望插入的關係數量來說太慢了。

我沒有訪問基礎節點ID的權限,只是我插入它們時給予它們的屬性。

有沒有更快的方法來做到這一點?

注意:目前我沒有使用索引(我還沒有計算出如何添加它們),但會在明天再次嘗試索引。我只是想知道是否有一種以某種方式批量插入關係的方法。

回答

7

第一個改進可能是將標籤分配給節點,以便您可以使用索引。如果沒有conceptId的索引,每次執行查詢時,它都會掃描400,000個節點兩次,一次爲您匹配的兩個節點中的每一個節點掃描一次。推測根據您的查詢,你可以給你的節點標籤:Concept和索引conceptId屬性如下

MATCH (n) 
// WHERE HAS (n.conceptId) //if you have some nodes that don't represent concepts, and conceptId distinguishes the ones that do from others 
SET n:Concept 

那麼對於指數

CREATE INDEX ON :Concept(conceptId) 

,或者如果conceptId是一個唯一的標識值,你可以使用約束代替

CREATE CONSTRAINT ON (c:Concept) ASSERT c.conceptId IS UNIQUE 

一旦您設置了標籤並創建了索引,您就可以使用它們快速請查看您正在連接的節點。所有你需要做的就是在你的查詢中包含標籤和索引屬性。你已經使用了索引的屬性,因此添加標籤查詢變得

MATCH (a:Concept {ConceptId: '280844000'}), (b:Concept {ConceptId: '71737002'}) 
CREATE (a)-[:Is_a]->(b) 
RETURN a 

你可以閱讀更多有關架構in the Neo4j documentation

第二個改進可能是使用LOAD CSV,如@stephenmuss所示。

如果您在將來沒有基於csv文件的查詢,還有兩件事需要考慮。首先是參數化您的查詢。然後你HTTP調用看起來像這樣

POST http://localhost:7474/db/data/cypher 
Accept: application/json; charset=UTF-8 
Content-Type: application/json 
{"query": "MATCH (a { ConceptId: {a} }), (b { ConceptId: {b} }) CREATE (a)-[:Is_a]->(b) RETURN a","params":{"a":"280844000","b":"71737002"}} 

這允許執行引擎創建一次執行計劃,該結構的第一個查詢。下一次您發出具有相同結構的查詢時,緩存的執行計劃將被重用。這將顯着提高具有相同結構的重複查詢的性能。

最後一件事是沿着@ulkas評論,批量插入。 LOAD CSV更快的一個原因是它在一個事務中執行多個操作。您可以使用事務性密碼端點執行類似的操作。然後,您可以對每個事務執行幾千條小型語句,這對於在數據庫上運行具有更高的性能,並且還可以減少線路上的開銷。設計事務端點的有效負載並處理異常稍微複雜一些。下面是一個簡單的例子,您可以在Neo4j manual pages中閱讀更多關於它的內容。

POST http://localhost:7474/db/data/transaction 
Accept: application/json; charset=UTF-8 
Content-Type: application/json 
{"statements":[ 
    {"statement":"MATCH (a:Concept {ConceptId: {a}}), (b:Concept {ConceptId: {b}}) CREATE (a)-[:Is_a]->(b) RETURN a","parameters":{"a":"280844000","b":"71737002"}}, 
    {"statement":"MATCH (a:Concept {ConceptId: {a}}), (b:Concept {ConceptId: {b}}) CREATE (a)-[:Is_a]->(b) RETURN a","parameters":{"a":"199401294","b":"51233509"}} 
]} 

服務器返回的新事務的location,說"http://localhost:7474/db/data/transaction/1"。您可以繼續執行同一交易中的語句

POST http://localhost:7474/db/data/transaction/1 
Accept: application/json; charset=UTF-8 
Content-Type: application/json 
{"statements":[...]} 

當您完成您的提交。提交調用也可以包含語句。

POST http://localhost:7474/db/data/transaction/1/commit 
Accept: application/json; charset=UTF-8 
Content-Type: application/json 
{"statements":[...]} 
+2

這是一種讓你相信StackOverflow的使命。 – David

+0

爲每個節點賦予「Concept」標籤並對「ConceptId」賦予一個唯一的約束將性能提高了幾個數量級。謝謝! – David

3

如果您使用的是Neo4j 2.1+,我認爲您的最佳選擇是使用LOAD CSV

然後,您可以使用類似下面的語法

USING PERIODIC COMMIT 
LOAD CSV WITH HEADERS FROM "file:/path/to/file.csv" AS csvLine 
MATCH (a{ConceptId: csvLine.aId}), (b{ConceptId: csvLine.bId}) 
CREATE (a)-[:Is_a]->(b) 

我建議檢查出docs for importing csv files

+0

啊,完全錯過了!謝謝。 – David

+0

,或者可能會設置一個「批量」插入,其中一次調用db將包含多個創建查詢。 – ulkas