Neo4jの大量データインポート 2020 #neo4j
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
Neo4jは、CSVファイルから大量データのインポートを行います。
CSVファイルから大量データをインポートする方式は、LOAD CSVとneo4j-admin importの2つがあります。
ここでは、LOAD CSVとneo4j-admin importの使用法をNeo4j v4.0(2020-01-15リリース)をベースに紹介します。
LOAD CSVとneo4j-admin import
LOAD CSV
LOAD CSVは、CSVファイルからデータを取り込み、既存のデータベースに対して追加や更新、削除を行います。CSVから読み取ったデータをCypherに埋め込んでデータベースに投入しているために非常に柔軟な処理が可能です。
neo4j-admin import
neo4j-admin import(旧neo4j-import)は、CSVファイルからデータを取り込み、新規のデータベースを構築します。この方式では、ヘッダファイル、ノードファイル、リレーショナルファイルなどを別々に作成し、一回のインポートでデータベースファイルを生成します。LOAD CAVよりも数十倍以上高速です。
load csvとneo4j-admin importの比較
項目 | load csv | neo4j-admin import |
データファイルフォーマット | CSV | CSV |
データベースのステータス | オンライン | オフライン |
対応データサイズ | スモールサイズ | ラージサイズ |
パフォーマンス | 低速 | 高速 |
データの追加 | 出来る(繰り返し実行可能) | 出来ない(一過性) |
データベースの初期化 | 不要 | 必要(データベースファイルが存在しては行けない) |
CypherQL | 必要 | 不要 |
CRUD | CUD(作成 更新 削除) | C(作成) |
ユースケース | 差分データの追加・更新・削除 | 初期データのマイグレーション |
LOAD CSVの使用法
[参考]
https://neo4j.com/docs/cypher-manual/current/clauses/load-csv/#query-load-csv
LOAD CSVは2種類の実行方法があります。
- Neo4jブラウザーからの実行
- cypher-shellからの実行(neo4j-shellはcypher-shellに統合)。
CSVファイルのフォーマット
LOAD CSV で規定しているCSVファイルのフォーマットは、次のとおりです。
- UTF-8
- 改行は\n(Linux)/\r\n(windows)
- デフォルトのデリミタ―はコンマ(,)です
- 任意のデリミタ―も利用可能です。例えば、「IELDTERMINATOR ';'」のように定義できます
- 文字列はダブルクォテーション("string")で囲むのが原則です。例えば、数字を文字として扱う場合や文字列に引用符がついている場合などです。特にデータタイプを意識しなくても良い場合は、省略しても問題ありません。
- 引用符付きの文字列が使用可能です。読み込むときに、引用符は自動的に外されます。「The "Symbol"」のような結果を期待するデータの読み込みは「"The ""Symbol"""」のように設定します。
- エスケープ文字は「\」を使います。上記の属性値の場合は、「"The \"Symbol\""」ように表現できます
- CSVファイルの中にヘッダ(フィールド名)を指定する場合は、1行目に指定する必要があります。CSVファイルの中にヘッダを指定せず、ヘッダだけを別ファイルに指定することや、構文のなかで属性名を定義し、CSVファイルのなかの属性値とマッピングさせることも可能です。
では、LOAD CSVの例を見てみましょう。
例文1: ヘッダ無しのタイプ
ヘッダーなしのCSVファイルからインポートする方法です。
[artists.csv]
1,ABBA,1992 2,Roxette,1986 3,Europe,1979 4,The Cardigans,1992
[クエリ]
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/4.0/csv/artists.csv' AS line CREATE (:Artist { name: line[1], year: toInteger(line[2])})
- ヘッダーが存在しないためにデータはリストとしてマッピングされます。
CSVファイルを${NEO4J_HOME}/import/artists.csvのように格納し、次のようにクエリを書くこともできます。
LOAD CSV FROM 'file:///artists.csv' AS line CREATE (:Artist { name: line[1], year: toInteger(line[2])})
[実行結果]
+-------------------+ | No data returned. | +-------------------+ Nodes created: 4 Properties set: 8 Labels added: 4
例文2:ヘッダ有りのタイプ
[artists-with-headers.csv]
Id,Name,Year 1,ABBA,1992 2,Roxette,1986 3,Europe,1979 4,The Cardigans,1992
[クエリ]
LOAD CSV WITH HEADERS FROM 'file:///artists-with-headers.csv' AS line CREATE (:Artist { name: line.Name, year: toInt(line.Year)})
- name: line.Nameは、「属性名:識別子.ヘッダ名」を意味します。
[実行結果]
+-------------------+ | No data returned. | +-------------------+ Nodes created: 4 Properties set: 8 Labels added: 4
例文3:デリミタ―をカスタマイズ
[artists-fieldterminator.csv]
"1";"ABBA";"1992" "2";"Roxette";"1986" "3";"Europe";"1979" "4";"The Cardigans";"1992"
[クエリ]
LOAD CSV FROM 'file:////artists-fieldterminator.csv' AS line FIELDTERMINATOR ';' CREATE (:Artist { name: line[1], year: toInteger(line[2])})
- FIELDTERMINATOR ';'→コンマの変わりにセミコロンをデリミタ―で使っています
[実行結果] +-------------------+ | No data returned. | +-------------------+ Nodes created: 4 Properties set: 8 Labels added: 4
例文4:トランザクション処理の行数制限
LOAD CSV文で大量のデータを取り込むときは、1回のトラザクションで処理するデータの行数を制限し、Javaのヒープメモリが溢れないようにします。デフォルトは、1000行です。
USING PERIODIC COMMIT [行数] LOAD CSV FROM 'file:///artists.csv' AS line CREATE (:Artist { name: line[1], year: toInteger(line[2])})
例文5:エスケープ文字対応
[データファイル]
artists-with-escaped-char.csv
"1","The ""Symbol""","1992"
LOAD CSV FROM 'file:///artists-with-escaped-char.csv' AS line CREATE (a:Artist { name: line[1], year: toInteger(line[2])}) RETURN a.name AS name, a.year AS year, size(a.name) AS size
neo4j-admin importの使用法
neo4j-admin importは常に新しいデータベースを生成します。
- Neo4jサーバーのプロセスをストップする必要があります
- 既存のデータベースに対するインポートはできません
- 既存のデータベースのディレクトリを再利用したい場合は、データベースファイルを削除して下さい
- 新規のデータベースを作成する場合は、実行オプションとして「--database=
」のように指定します
CSVファイルのフォーマット
基本的な内容はLOAD CSVと同じですが、Cypherが使えないのであらかじめグラフ形式のデータである必要があります。
- ノードファイルでは、データベース全体でユニークなIDを付与し、ラベルを指定する必要があります
- リレーションシップファイルでは、始点ノードIDと終点ノードIDとリレーションシップのタイプを指定する必要があります
例文1:標準的なタイプ
ノードファイルとリレーションシップファイルで構成します。
[movies.csv]
movieId:ID,title,year:int,:LABEL tt0133093,"The Matrix",1999,Movie tt0234215,"The Matrix Reloaded",2003,Movie;Sequel tt0242653,"The Matrix Revolutions",2003,Movie;Sequel
- ヘッダ付きのノードのデータです
- movieId:ID→movieIdフィールドはIDであるという定義であり、IDはデータベース全体で一意である必要があります。
- year:int→yearフィールドは数字であるという定義です
- :LABEL→このフィールドはラベルであるという定義です
- Movie;Sequel→リスト表現であり、階層型のラベルであるという意味です(:Movie:Sequel)
[actors.csv]
personId:ID,name,:LABEL keanu,"Keanu Reeves",Actor laurence,"Laurence Fishburne",Actor carrieanne,"Carrie-Anne Moss",Actor
[roles.csv]
:START_ID,role,:END_ID,:TYPE keanu,"Neo",tt0133093,ACTED_IN keanu,"Neo",tt0234215,ACTED_IN keanu,"Neo",tt0242653,ACTED_IN laurence,"Morpheus",tt0133093,ACTED_IN laurence,"Morpheus",tt0234215,ACTED_IN laurence,"Morpheus",tt0242653,ACTED_IN carrieanne,"Trinity",tt0133093,ACTED_IN carrieanne,"Trinity",tt0234215,ACTED_IN carrieanne,"Trinity",tt0242653,ACTED_IN
- ヘッダ付きのリレーションシップのデータです
- :START_ID→このフィールドはグラフの始点ノードのIDであるという定義です
- role→このフィールドは映画のなかの配役であり、リレーションシップの属性という定義です
- :END_ID→このフィールドはグラフの終点ノードのIDであるという定義です
- :TYPE→このフィールドは関係性のタイプであるという定義です
次のようにインポートを実行します。
bin/neo4j-admin import --nodes=import/movies.csv --nodes=import/actors.csv \ --relationships=import/roles.csv
例文2:オプション指定
デリミターの設定をセミコロン(;)にし、属性値はシングルクォーテーション(')、ラベルはバーティカルバー(|)に区切って配列にしています。
[movies2.csv]
movieId:ID;title;year:int;:LABEL tt0133093;'The Matrix';1999;Movie tt0234215;'The Matrix Reloaded';2003;Movie|Sequel tt0242653;'The Matrix Revolutions';2003;Movie|Sequel
[actors2.csv]
personId:ID;name;:LABEL keanu;'Keanu Reeves';Actor laurence;'Laurence Fishburne';Actor carrieanne;'Carrie-Anne Moss';Actor
[roles2.csv]
:START_ID;role;:END_ID;:TYPE keanu;'Neo';tt0133093;ACTED_IN keanu;'Neo';tt0234215;ACTED_IN keanu;'Neo';tt0242653;ACTED_IN laurence;'Morpheus';tt0133093;ACTED_IN laurence;'Morpheus';tt0234215;ACTED_IN laurence;'Morpheus';tt0242653;ACTED_IN carrieanne;'Trinity';tt0133093;ACTED_IN carrieanne;'Trinity';tt0234215;ACTED_IN carrieanne;'Trinity';tt0242653;ACTED_IN
次のようにインポートを実行します。
bin/neo4j-admin import --nodes=import/movies2.csv --nodes=import/actors2.csv \ --relationships=import/roles2.csv --delimiter=";" --array-delimiter="|" --quote="'"
例文3:ヘッダを分離
次はヘッダを別ファイルしたケースです。
[movies3-header.csv]
movieId:ID,title,year:int,:LABEL
[movies3.csv]
tt0133093,"The Matrix",1999,Movie tt0234215,"The Matrix Reloaded",2003,Movie;Sequel tt0242653,"The Matrix Revolutions",2003,Movie;Sequel
[actors3-header.csv]
personId:ID,name,:LABEL
[actors3.csv]
keanu,"Keanu Reeves",Actor laurence,"Laurence Fishburne",Actor carrieanne,"Carrie-Anne Moss",Actor
[roles3-header.csv]
:START_ID,role,:END_ID,:TYPE
[roles3.csv]
keanu,"Neo",tt0133093,ACTED_IN keanu,"Neo",tt0234215,ACTED_IN keanu,"Neo",tt0242653,ACTED_IN laurence,"Morpheus",tt0133093,ACTED_IN laurence,"Morpheus",tt0234215,ACTED_IN laurence,"Morpheus",tt0242653,ACTED_IN carrieanne,"Trinity",tt0133093,ACTED_IN carrieanne,"Trinity",tt0234215,ACTED_IN carrieanne,"Trinity",tt0242653,ACTED_IN
次のようにインポートを実行します。
bin/neo4j-admin import --nodes=import/movies3-header.csv,import/movies3.csv \ --nodes=import/actors3-header.csv,import/actors3.csv \ --relationships=import/roles3-header.csv,import/roles3.csv
- ヘッダファイルとデータファイルをコンマ区切りで設定しています
例文4: 複数のデータファイル
同ノードのファイル、リレーションシップのファイルが複数存在する場合もあります。
[movies4-header.csv]
movieId:ID,title,year:int,:LABEL
[movies4-part1.csv]
tt0133093,"The Matrix",1999,Movie tt0234215,"The Matrix Reloaded",2003,Movie;Sequel
[movies4-part2.csv]
tt0242653,"The Matrix Revolutions",2003,Movie;Sequel
[actors4-header.csv]
personId:ID,name,:LABEL
[actors4-part1.csv]
keanu,"Keanu Reeves",Actor laurence,"Laurence Fishburne",Actor
[actors4-part2.csv]
carrieanne,"Carrie-Anne Moss",Actor
[roles4-header.csv]
:START_ID,role,:END_ID,:TYPE
[roles4-part1.csv]
keanu,"Neo",tt0133093,ACTED_IN keanu,"Neo",tt0234215,ACTED_IN keanu,"Neo",tt0242653,ACTED_IN laurence,"Morpheus",tt0133093,ACTED_IN laurence,"Morpheus",tt0234215,ACTED_IN
[roles4-part2.csv]
laurence,"Morpheus",tt0242653,ACTED_IN carrieanne,"Trinity",tt0133093,ACTED_IN carrieanne,"Trinity",tt0234215,ACTED_IN carrieanne,"Trinity",tt0242653,ACTED_IN
次のようにインポートを実行します。
bin/neo4j-admin import --nodes=import/movies4-header.csv,import/movies4-part1.csv,import/movies4-part2.csv \ --nodes=import/actors4-header.csv,import/actors4-part1.csv,import/actors4-part2.csv \ --relationships=import/roles4-header.csv,import/roles4-part1.csv,import/roles4-part2.csv
- コンマ区切りでファイルを並べます
例文5:正規表現を使ったファイル指定
次のように「例文4」のような構文のファイル名を正規表現でシンプルに書き換えることが出来ます。
bin/neo4j-admin import --nodes=import/movies4-header.csv,import/movies4-part.* \ --nodes=import/actors4-header.csv,import/actors4-part.* \ --relationships=import/roles4-header.csv,import/roles4-part.*
例文6:ノードのシンプルラベル設定
ノードファイルとラベルが1:1の関係である場合、ファイルのなかにはラベルを持たず、インポート文に設定することが出来ます。
[movies5a.csv]
movieId:ID,title,year:int tt0133093,"The Matrix",1999
[sequels5a.csv]
movieId:ID,title,year:int tt0234215,"The Matrix Reloaded",2003 tt0242653,"The Matrix Revolutions",2003
[actors5a.csv]
personId:ID,name keanu,"Keanu Reeves" laurence,"Laurence Fishburne" carrieanne,"Carrie-Anne Moss"
[roles5a.csv]
:START_ID,role,:END_ID,:TYPE keanu,"Neo",tt0133093,ACTED_IN keanu,"Neo",tt0234215,ACTED_IN keanu,"Neo",tt0242653,ACTED_IN laurence,"Morpheus",tt0133093,ACTED_IN laurence,"Morpheus",tt0234215,ACTED_IN laurence,"Morpheus",tt0242653,ACTED_IN carrieanne,"Trinity",tt0133093,ACTED_IN carrieanne,"Trinity",tt0234215,ACTED_IN carrieanne,"Trinity",tt0242653,ACTED_IN
次のようにインポートを実行します。
bin/neo4j-admin import --nodes=Movie=import/movies5a.csv \ --nodes=Movie:Sequel=import/sequels5a.csv \ --nodes=Actor=import/actors5a.csv \ --relationships=import/roles5a.csv
- --nodes=Movie=import/movies5a.csvのようにファイル名の前にラベルを設定しています(=Movie=)
例文7:リレーションシップのシンプルタイプ指定
「例文6」のようなやり方はリレーションシップにも適用できます。
[movies5b.csv]
movieId:ID,title,year:int,:LABEL tt0133093,"The Matrix",1999,Movie tt0234215,"The Matrix Reloaded",2003,Movie;Sequel tt0242653,"The Matrix Revolutions",2003,Movie;Sequel
[actors5b.csv]
personId:ID,name,:LABEL keanu,"Keanu Reeves",Actor laurence,"Laurence Fishburne",Actor carrieanne,"Carrie-Anne Moss",Actor
[roles5b.csv]
:START_ID,role,:END_ID keanu,"Neo",tt0133093 keanu,"Neo",tt0234215 keanu,"Neo",tt0242653 laurence,"Morpheus",tt0133093 laurence,"Morpheus",tt0234215 laurence,"Morpheus",tt0242653 carrieanne,"Trinity",tt0133093 carrieanne,"Trinity",tt0234215 carrieanne,"Trinity",tt0242653
次のようにインポートを実行します。
neo4j_home$ bin/neo4j-admin import --nodes=import/movies5b.csv --nodes=import/actors5b.csv \ --relationships=ACTED_IN=import/roles5b.csv
- --relationships=ACTED_IN=import/roles5b.csvのようにファイル名の前にタイプを設定しています(=ACTED_IN=)
例文8:属性のデータタイプ指定
多様な属性のデータタイプをヘッダに指定できます。
[movies6.csv]
movieId:ID,title,year:int,:LABEL tt0099892,"Joe Versus the Volcano",1990,Movie
[actors6.csv]
personId:ID,name,:LABEL meg,"Meg Ryan",Actor
[roles6.csv]
[:START_ID,roles:string[],:END_ID,:TYPE] meg,"DeDe;Angelica Graynamore;Patricia Graynamore",tt0099892,ACTED_IN
bin/neo4j-admin import --nodes=import/movies6.csv --nodes=import/actors6.csv \ --relationships=import/roles6.csv
例文9:ID操作の例外
基本的にノードIDはデータベース全体で一意性を持つべきですが、同ノードファイルの中で一意であればいい場合もあります。
ノードファイルが一意のシーケンシャル番号、自動インクメンタルなどで生成された場合です。
[movies7.csv]
movieId:ID(Movie-ID),title,year:int,:LABEL 1,"The Matrix",1999,Movie 2,"The Matrix Reloaded",2003,Movie;Sequel 3,"The Matrix Revolutions",2003,Movie;Sequel
- IDにMovie-IDのように識別子を指定します
[actors7.csv]
personId:ID(Actor-ID),name,:LABEL 1,"Keanu Reeves",Actor 2,"Laurence Fishburne",Actor 3,"Carrie-Anne Moss",Actor
roles7.csv
:START_ID(Actor-ID),role,:END_ID(Movie-ID) 1,"Neo",1 1,"Neo",2 1,"Neo",3 2,"Morpheus",1 2,"Morpheus",2 2,"Morpheus",3 3,"Trinity",1 3,"Trinity",2 3,"Trinity",3
- START_ID(Actor-ID)とEND_ID(Movie-ID)のように設定します
bin/neo4j-admin import --nodes=import/movies7.csv --nodes=import/actors7.csv \ --relationships=ACTED_IN=import/roles7.csv
neo4j-admin importのオプション
neo4j-admin importには、データを正確にインポートするためのオプションが多数あります。
こちらを参照してください。
[参考]
https://neo4j.com/docs/operations-manual/4.0/tools/import/options/
neo4.confの設定
大量データをインポートする時は、メモリ割り当てが気になります。
neo4j-admin memrecは、該当マシンのリソースをチェックし、初期値を割り出してくれます。
bin/neo4j-admin memrec … Based on the above, the following memory settings are recommended: dbms.memory.heap.initial_size=5100m dbms.memory.heap.max_size=5100m dbms.memory.pagecache.size=2900m dbms.tx_state.max_off_heap_memory=4000m …
次の3つをconf/neo4j.confに設定します。
dbms.tx_state.max_off_heap_memoryは、トランザクションの状態を管理するoff-heapメモリでDefaultで2Gもありますので無視して下さい。
dbms.memory.heap.initial_size=5100m dbms.memory.heap.max_size=5100m dbms.memory.pagecache.size=2900m
以上、Neo4jの大量データインポートでした。