1. 程式人生 > >使用neo4j圖資料庫的import工具匯入資料 -方法和注意事項

使用neo4j圖資料庫的import工具匯入資料 -方法和注意事項

背景

最近我在嘗試儲存知識圖譜的過程中,接觸到了Neo4j圖資料庫,這裡我摘取了一段Neo4j的簡介:

Neo4j是一個高效能的,NOSQL圖形資料庫,它將結構化資料儲存在網路上而不是表中。它是一個嵌入式的、基於磁碟的、具備完全的事務特性的Java持久化引擎,但是它將結構化資料儲存在網路(從數學角度叫做圖)上而不是表中。Neo4j也可以被看作是一個高效能的圖引擎,該引擎具有成熟資料庫的所有特性。程式設計師工作在一個面向物件的、靈活的網路結構下而不是嚴格、靜態的表中——但是他們可以享受到具備完全的事務特性、企業級的資料庫的所有好處。

在下載了Neo4j Server(3.4.1)以後,我開始著手把手頭的三元組資料儲存進neo4j的資料庫中,用的是python的py2neo庫,我的思路是:讀取檔案,將每行的實體抽取出來,在圖中查詢是否有該(兩個)實體節點,如果不存在就插入節點,然後插入該行三元組表示的邊。
但是這樣做的效率很低(我的圖要至少連續一個月才能存完)。我分析了一下,原因在於:每次插入實體節點都需要先查詢圖中是否存在該實體節點,隨著圖的增大,查詢所需的時延也越來越長。
在查了

官方文件以後,我找到了一個高效率的匯入資料的方法–neo4j的import工具,這裡我將我在匯入過程中遇到的問題和我的解決方案和分析分享出來,供大家參考。

匯入方法

import工具命令為如下格式:

 neo4j-admin import [--mode=csv][--database=<name>][--additional-config=<config-file-path>][--report-file=<filename>][--nodes[:Label1:Label2]=<"file1,file2,...">]
                          
[--relationships[:RELATIONSHIP_TYPE]=<"file1,file2,...">] [--id-type=<STRING|INTEGER|ACTUAL>][--input-encoding=<character-set>][--ignore-extra-columns[=<true|false>]] [--ignore-duplicate-nodes[=<true|false>]]
[--ignore-missing-nodes[=<true|false>]] [--multiline-fields[=<true|false>]] [--delimiter=<delimiter-character>][--array-delimiter=<array-delimiter-character>][--quote=<quotation-character>][--max-memory=<max-memory-that-importer-can-use>][--f=<File containing all arguments to this import>][--high-io=<true/false>]

或:

neo4j-admin import --mode=database [--database=<name>][--additional-config=<config-file-path>][--from=<source-directory>]

方括號內為可以選擇的引數,其中我們常用的是第一種格式,即從獨立的檔案裡匯入圖資料,常用引數為--nodes--relationships,分別用來引入節點的CSV檔案和邊的CSV檔案。
舉個例子:

bin/neo4j-admin import --nodes <filepath of the csv file of nodes> --relationships <filepath of the csv file of relationships>

1. 生成CSV檔案

Neo4j的import工具要求資料使用CSV檔案儲存,因此在匯入資料前需要將資料轉乘CSV檔案。節點和關係需要不同的檔案,一種節點的CSV檔案可以分為多個檔案儲存,傳遞引數的時候需要按順序加上所有檔案的檔名(絕對路徑),工具讀取第一個檔案的表頭作為節點/邊的屬性名,該檔案剩下所有行以及後續檔案的所有行作為屬性值。多種節點(如包含不同屬性集合)的匯入需要分別為每一種節點分別引入(即使用多次--node引數)。
在我的需求中,節點和邊都只包含一個名稱(資料格式每行為<subject>\t<predicate>\t<object> .),因此我將節點和邊分別僅用一個CSV檔案儲存,使用python的csv庫,csv庫寫csv檔案的方法為(以下程式碼不可執行):

import csv

csvf = open(filepath,'w',newline='',encoding='utf-8')
w = csv.writer(csvf)
w.writerow((column_name_1, column_name_2, ...))#寫入表頭
for i in some_range:
    w.writerow((column_1, column_2, ...))#寫入行
csvf.close()

注意,writerow傳入的引數為含有多個字串的tuple,而不是多個字串。
我生成的CSV檔案表頭結構如下:
node.csv: name:ID(node), :LABEL
rel.csv: :START_ID(node), :END_ID(node), :TYPE,name

其中:

  • name:ID 表示該列的屬性名為name,ID
    表示該屬性是唯一標示一個實體的屬性(類似關係型資料庫中的主碼),括號表示一個id-group,即表示該ID唯一表示括號內種類的實體,而不是所有實體;
  • :LABEL 表示節點的標籤;
  • START_IDEND_ID 表示邊的起點和終點的ID,可以加上它們各自的id-group;
  • :TYPE 表示該邊的種類,注意種類個數不應超過65535。

2.使用import指令匯入

在CSV檔案準備就緒以後,開啟電腦的終端,執行:
1.改變工作目錄至Neo4j的根目錄,(開啟該目錄以後應當能看到bin, conf, data, import, lib等資料夾):

cd filepath_to_neo4j_home_directory

2.執行neo4j-admin import指令,注意:
在此之前應當保證在Neo4j的目錄下的data/databases/graph.db 下沒有檔案,即該指令要求資料庫為空;
Neo4j應當關閉,處於stopped狀態(關閉方法:終端在Neo4j的根目錄執行./bin/neo4j stop),
--nodes--relationships 後的檔名應當是絕對路徑。

bin/neo4j-admin import --nodes some_path_to/node.csv --relationships some_path_to/rel.csv

不出意料,該指令執行結果是:

import指令執行結果
3.重新啟動Neo4j,進入瀏覽器輸入IP+埠(預設為http://localhost:7474/)檢視結果,啟動的指令是:

./bin/neo4j start

如果正確操作,且不出意外的話,在瀏覽器中應當能查詢正確匯入的圖譜:
這裡寫圖片描述

總結&注意事項

1.傳入檔名的時候務必使用絕對路徑,否則將會丟擲以下錯誤:

Expected '--nodes' to have at least 1 valid item, but had 0 []
Expected '--relationships' to have at least 1 valid item, but had 0 []

2.使用neo4j-admin import指令匯入之前先將原資料庫從neo4j_home/data/databases/graph.db/中移除,即指令要求目錄下不含資料庫,否則指令無法執行;

3.在執行指令之前務必保證Neo4j處於關閉狀態,如果不確定可以在Neo4j根目錄下執行./bin/neo4j status 檢視當前狀態。如果資料庫未關閉,可能會導致資料庫即使成功匯入,也無法查詢到(我的經驗是這樣);

4.寫CSV檔案的時候務必確保所有的節點的CSV檔案的ID fileds的值都唯一、不重複(類似SQL中的primary key),並且確保所有的邊的CSV檔案的START_ID 和 END_ID都包含在節點CSV檔案中(參考SQL中的referential integrity constraint);

5.若要使用其他引數請參考官方的文件,我在下面的References中給出;

6.我的Neo4j執行環境是OS X系統,如果你使用的是其他作業系統,指令可能有所不同,比如啟動和終止服務的指令。

References: