なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

Neo4jでJOINクエリ

Neo4jでJOINクエリ

課題・やりたいこと

課題

やってること

レポーティング, OLAP

  • 関係するテーブルのデータの有無で条件分岐を作成する → 開発当初から条件分岐が多すぎてテストが辛い
  • 多少はSQLに対する慣れにより解消したが、しばらくソースを見ないと忘れるし追加があると追加自体が難しい
  • これはすでにSQLアンチパターンのスパゲッティクエリの状態である

なぜ、SQLでJOINしまくる羽目になるのか?

  • 正規化しているから、は1つの理由だが
  • そもそも論
    • つまり、関係データベースは名前に反して関係性を管理するのが苦手だ

https://image.slidesharecdn.com/rdbmstographsintro-151028191556-lva1-app6892/95/neo4jrdb-9-638.jpg?cb=1446059865

from グラフデータベース:Neo4j、そしてRDBからの移行手順の紹介 | PPT

やりたいこと

クエリを減らしてメンテナンス性をよくする、こんな風に

from How to do Joins in Apache Cassandra and DSE | DataStax

実践

まずはSQLでいうところのJOINクエリをやっていく、やること

  • データ設計の見直し
  • Neo4jはグラフDB操作用にCypher (Cypher (query language) - Wikipedia)というクエリ言語をもっている、基本はそいつを使ってクエリを実行する ** あとはSQLとの橋渡しをこの辺を見ながらやってみる

curiousraj.com

概要と手順

データ設計の見直し

だいたいは、ここの通りに実行 グラフデータベース:Neo4j、そしてRDBからの移行手順の紹介 | PPT

手順

  1. ノード間の関係を定義する(例)従業員 =(販売)=> 受注
  2. 外部キーを見つける
  3. (外部キー)-[:変化]->(関係)
  4. 外部キーを取り除く
  5. ジョインテーブルを見つける
  6. ジョインテーブルがそのまま関係データになる
  7. 属性付きのジョインテーブルは、プロパティ付きの関係データに変換
  8. グラフ化完了

使ったクエリ

プラットフォームの選択

Windowsの場合WSLを使用してDebianでインストールしたほうがいい気がする Neo4j Debian Packages

  • neo4j-shell
    • /etc/neo4j/neo4j.conf のコメントを外しておく
# Enable a remote shell server which Neo4j Shell clients can log in to.
dbms.shell.enabled=true
# The network interface IP the shell will listen on (use 0.0.0.0 for all interfaces).
dbms.shell.host=127.0.0.1
# The port the shell will listen on, default is 1337.
dbms.shell.port=1337

インストールした後はserviceコマンドで起動できる。

$ sudo service neo4j start
Neo4jのコンフィグまわり
  • デフォルトではCSVファイルインポートができないので、Settingsから以下の設定を有効化

これをやらないとクエリを投げてもLOADできない。

# Determines if Cypher will allow using file URLs when loading data using
# `LOAD CSV`. Setting this value to `false` will cause Neo4j to fail `LOAD CSV`
# clauses that load data from the file system.
dbms.security.allow_csv_import_from_file_urls=true
WSLの環境へヘッダありCSVを読み込む例

まず一度WSLのLinux環境へファイルを送る

$ sudo cp /mnt/c/tmp/xxx.csv /var/lib/neo4j/import/

その後LOADクエリを実行する

> LOAD CSV WITH HEADERS
FROM 'file:///xxx.csv' AS line
CREATE (:X { head1: line.head1 });

なんか読み込みめっちゃ早い

実際にRDBのデータをGDBに移行
  • テーブルのCSVデータを登録してインデックスを張る
LOAD CSV WITH HEADERS
FROM 'file:///table-A.csv' AS line
CREATE (:TableA { f_key: line.f_key
});
CREATE INDEX ON :TableA(f_key);

LOAD CSV WITH HEADERS
FROM 'file:///table-B.csv' AS line
CREATE (:TableB { f_key: line.f_key
});
CREATE INDEX ON :TableB(f_key);
  • 関係テーブルから関係性を取り出して設定する
LOAD CSV WITH HEADERS
FROM 'file:///relation.csv' AS line
WITH line.relation_from AS from_key
         , line.relation_to AS to_key

MERGE (from:TableA { f_key: from_key })
MERGE (to:TableB { f_key: to_key })
MERGE (from)-[r:RELATE]->(to);
  • さっそくJOINみたいなことをやる
-- マッチしたものだけ返す
MATCH (a:TableA)-[r:RELATE]->(b:TableB) return a,b;

-- マッチしなかったものも全件返す
OPTIONAL MATCH (a:TableA)-[r:RELATE]->(b:TableB) return a,b;

はまりポイント・イケてる点など

はまりポイント

  • CSVインポートが結構めんどくさい
  • relationを作成するときはインデックスを作成しないと遅い(処理が終わらない)

イケてる点

  • 意外に早い
  • RDBで関係テーブルを作成する際は関係の意味を複数持たせにくいが、Neo4jならば自由に持たせられる

懸念

  • 関係性がテーブルみたいにしっかりと残らないので、関係性の管理とかがちょいわかりにくい?