1. はじめに
何周回遅れかわからないですが、最近 NoSQL (Mongo, Neo4j) にはまっています!
グラフ型 NoSQL データベース Neo4j の勉強を始めて、ちょっと可視化アプリでも作ってみるかと、Python の Flask と 以下の neovis.js を使って作成しようとしたのですが、
neovis.js がメジャーバージョンアップ (v2.0) したらしく、色々と設定項目が変わって Web で使い方を調べるのに苦労したので、ここで紹介します。
先に結果だけ貼ると、Neo4j にある movie データサンプルを neovis.js を使って可視化したものが以下になります(オレンジが映画、紫は人名です)。
このページでは neovis.js を使って Neo4j のデータを可視化する方法を中心に紹介します。Flask を使ったアプリケーションの作りこみについては行っていません。
2. Neo4j のセットアップ
Docker を使ったNeo4j のセットアップ方法を紹介します。すでに Neo4j の実行環境がある方は本章は飛ばしてください。
以下の docker-compose.yml
ファイルを準備し、
version: '3'
services:
neo4j:
container_name: neo4j
image: neo4j
ports:
- 7474:7474
- 7687:7687
volumes:
- ./data/data:/data
- ./data/logs:/logs
- ./data/conf:/conf
environment:
# default username and password "neo4j"
# neo4j password setting (NEO4J_AUTH=neo4j/<password>)
- NEO4J_AUTH=neo4j/neo4j_password
以下のコマンドで起動します。
$ docker-compose up -d
http://localhost:7474/
から Neo4j Browser を開くことができるので、docker-compose.yml
で指定した ユーザ名とパスワードを使用してログインしてください。
記載時は、image として neo4j:5.5.0-community を使用しました。
3. Flask + neovis.js でのグラフの可視化方法
3.1. データセットの準備
ここから Neo4j に入っているサンプルデータセットを neovis.js で可視化します。
Neo4j Browser から
$ :play movies
と実行し、表示される Chpher スクリプトを叩いて、movie データセットを作成してください。
$ MATCH (n) RETURN n
で以下のようなグラフが確認できます。
3.2. Flask + neovis.js での基本の描画
以下のコマンドで、Python 環境に Flask をインストールします。
$ pip install flask
Flask 用の アプリケーションファイルを準備し、
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
公式にある advanced-example.html を参考にした以下のhtml ファイルを、templates
フォルダに作成します。
<!doctype html>
<!-- https://github.com/neo4j-contrib/neovis.js/blob/master/examples/advanced-example.html -->
<html>
<head>
<title>Neovis.js Simple Example</title>
<style type="text/css">
html,
body {
font: 16pt arial;
}
#viz {
width: 800px;
height: 800px;
border: 1px solid lightgray;
font: 22pt arial;
}
</style>
<script src="https://unpkg.com/neovis.js@2.0.2"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
</head>
<body onload="draw()">
<div id="viz"></div>
Cypher query: <textarea rows="4" cols=50 id="cypher"></textarea><br>
<input type="submit" value="Submit" id="reload">
<input type="submit" value="Stabilize" id="stabilize">
</body>
<script type="text/javascript">
let neoViz;
function draw() {
const config = {
containerId: "viz",
neo4j: {
// neo4jの接続設定
serverUrl: "neo4j://localhost:7687",
serverUser: "neo4j",
serverPassword: "neo4j_password",
},
visConfig: {
nodes: {
borderWidth: 2,
borderWidthSelected: 5,
},
edges: {
// 線はグレーの矢印にする
color: 'gray',
arrows: {
to: { enabled: true }
},
},
},
labels: {
Person: {
label: "name",
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
// visのconfig設定を追加する
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml,
},
static: {
// ノードの背景色
color: "#beaed4",
},
}
},
Movie: {
label: "title",
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml,
},
static: {
// ノードの背景色
color: "#fdc086",
// ノードの形を四角にする
shape: 'box',
}
}
}
},
relationships: {
ACTED_IN: {
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml
},
}
},
},
// 初期で実行するCypherクエリ
initialCypher: "MATCH (p:Person)-[r]->(m:Movie) RETURN *"
};
viz = new NeoVis.default(config);
viz.render();
console.log(viz);
}
$('#reload').click(function () {
// Cypher query に入力されたクエリを読み込んで再描画する
var cypher = $('#cypher').val();
if (cypher.length > 3) {
viz.renderWithCypher(cypher);
} else {
console.log('reload');
viz.reload();
}
});
$('#stabilize').click(function () {
// うねうねと動く描画を停止する
viz.stabilize();
});
</script>
</html>
以下のコマンドでアプリケーションを実行し、http://localhost:5000/
にアクセスしてください。
$ python app.py
上記図では、ノードの重なりが多いなと感じると思いますが、あとで改善する設定を紹介します。
Cypher query の入力ボックスから、描画を変更することができ、例えばトムハンクスだけを表示するようにすると
MATCH (p:Person {name: "Tom Hanks"})-[r]->(m:Movie) RETURN *
以下のようになります。
3.3. neovis.js の設定についての補足
ここからは先ほどの例の補足と、3.2の例では省略した追加の設定項目について紹介します。
3.3.1. vis.js について
neovis.js は vis.js のラッパーなので、多くの設定項目名は vis.js に由来しているようです。よくわからない点があれば、vis.js で検索するとよいと思います。
3.3.2. Neo4jとの接続設定
以下で Neo4j との接続設定を行います。
neo4j: {
// neo4jの接続設定
serverUrl: "neo4j://localhost:7687",
serverUser: "neo4j",
serverPassword: "neo4j_password",
},
3.3.3. ノードと線の設定
labels
でノードの設定を、relationships
で接続線の設定を行います。
まずノードは以下のように記載します。
labels: {
ラベル名: {
label: (ノードの中央に表示するプロパティ名)
}
}
例えばラベル Person
の場合は,名前 name
をノード名として表示してほしいので以下のようになります。
labels: {
Person: {
label: "name"
}
}
また基本の例では表示がうるさくなるので省略したのですが、接続線について、線名を追加したいときは以下のようにします。
relationships: {
ACTED_IN: {
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
static: {
label: "ACTED_IN",
}
}
}
}
ACTED_IN の線名を追加すると下記のようになりました。
3.3.4. ノードの色とブロックの形の変更
公式 のREADME にあるように、あるラベルのあるプロパティに応じて色を変えたいときは、以下のように group
にそのプロパティ名を設定します。
labels: {
Character: {
label: "name",
group: "community",
}
}
一方で、今回は label の種類ごと(Person, Movieなど)に色を指定したかったので、その場合は以下のように [NeoVis.NEOVIS_ADVANCED_CONFIG]
の static.color
に色コードを指定します(color
は red, blue
などの色名にも対応しています)。
labels: {
Person: {
label: "name",
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
static: {
// ノードの背景色
color: "#beaed4",
}
}
}
}
[NeoVis.NEOVIS_ADVANCED_CONFIG]
が何なのか、詳しい説明を行ったページが見つからなかったのですが、static
に項目を設定することにより,vis.js で定義されている オプション項目 を直接定数で書き換えられるようです。
ノードの形状は、color
と同じように shape
で変更できます。
static: {
// ノードの背景色
color: "#fdc086",
// ノードの形を四角にする
shape: 'box',
}
3.3.5. ツールチップの表示
カーソルをあてたときに、ノードや線のプロパティを表示させるには、以下のように title: NeoVis.objectToTitleHtml
を追加します。
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
// visのconfig設定を追加する
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml,
},
}
Speed Racer にカーソルをあてると以下のように表示されます。
3.3.6. ノードの 配置
ノードの配置をツリー構造にしたいときは,visConfig
に以下のような layout
を追加します。
visConfig: {
layout: {
hierarchical: {
enabled: true,
direction: "LR",
sortMethod: "directed",
levelSeparation: 300 //階層間の距離(ちょっと広くする)
}
},
},
3.3.7. ノードの重なりを減らす
デフォルト設定だと、ノードがたくさんあるときに重なってしまうので、visConfig
に以下のような physics
を追加することでノードの間隔を広げます。
visConfig: {
edges: {
smooth: {
forceDirection: "none"
}
},
physics: {
barnesHut: {
gravitationalConstant: -7000,
springLength: 300,
// avoidOverlap: 0.5
},
minVelocity: 0.75
}
}
上記の physics
の設定については Playing with Physics で、パラメータの数値を変更しながら動作確認することができます。
avoidOverlap: 0.5
については入れた方がさらに重ならない気がするのですが、実行時にかなりうねうねとするのでコメントアウトしています。
変更前と変更後で比較を行ったものか以下になります。
初期設定
変更後
3.3.8. コールバック処理
ノードをクリックしたときに,コールバック処理を行いたいときは以下を追加します。例として、クリックしたノードの情報をコンソールログに出力しました。
// クリックしたノードの情報をコンソールに出すコールバック。
viz.registerOnEvent("completed", (e) => {
// 処理が完了しないと、viz.network.onがないと言われるので、completedしてから設定する。
viz.network.on("click", function (params) {
var ids = params.nodes;
var clickedNodes = viz.nodes.get(ids);
console.log('clicked nodes:', clickedNodes);
});
});
3.4. 最終的な html ファイル
前節で色々と紹介した、設定もりもりの html ファイルを以下に貼っておきます。
<!doctype html>
<!-- https://github.com/neo4j-contrib/neovis.js/blob/master/examples/advanced-example.html -->
<html>
<head>
<title>Neovis.js Simple Example</title>
<style type="text/css">
html,
body {
font: 16pt arial;
}
#viz {
width: 900px;
height: 800px;
border: 1px solid lightgray;
font: 22pt arial;
}
</style>
<script src="https://unpkg.com/neovis.js@2.0.2"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
</head>
<body onload="draw()">
<div id="viz"></div>
Cypher query: <textarea rows="4" cols=50 id="cypher"></textarea><br>
<input type="submit" value="Submit" id="reload">
<input type="submit" value="Stabilize" id="stabilize">
</body>
<script type="text/javascript">
let neoViz;
function draw() {
const config = {
containerId: "viz",
neo4j: {
// neo4jの接続設定
serverUrl: "neo4j://localhost:7687",
serverUser: "neo4j",
serverPassword: "neo4j_password",
},
visConfig: {
nodes: {
borderWidth: 2,
borderWidthSelected: 5,
},
edges: {
// 線はグレーの矢印にする
color: 'gray',
arrows: {
to: { enabled: true }
},
smooth: {
forceDirection: "none"
}
},
physics: {
barnesHut: {
gravitationalConstant: -7000,
springLength: 300,
// avoidOverlap: 0.5
},
minVelocity: 0.75
},
// layout: {
// hierarchical: {
// enabled: true,
// direction: "LR",
// sortMethod: "directed",
// levelSeparation: 300 //階層間の距離(ちょっと広くする)
// }
// },
},
labels: {
Person: {
label: "name",
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
// visのconfig設定を追加する
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml,
},
static: {
// ノードの背景色
color: "#beaed4",
},
}
},
Movie: {
label: "title",
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml,
},
static: {
// ノードの背景色
color: "#fdc086",
// ノードの形を四角にする
shape: 'box',
}
}
}
},
relationships: {
ACTED_IN: {
[NeoVis.NEOVIS_ADVANCED_CONFIG]: {
function: {
// ツールチップに情報を表示する
title: NeoVis.objectToTitleHtml
},
static: {
label: "ACTED_IN",
// color: "red"
}
}
},
},
// 初期で実行するCypherクエリ
initialCypher: "MATCH (p:Person)-[r]->(m:Movie) RETURN *"
};
viz = new NeoVis.default(config);
viz.render();
console.log(viz);
// クリックしたノードの情報をコンソールに出すコールバック。
viz.registerOnEvent("completed", (e) => {
// 処理が完了しないと、viz.network.onがないと言われるので、completedしてから設定する。
viz.network.on("click", function (params) {
var ids = params.nodes;
var clickedNodes = viz.nodes.get(ids);
console.log('clicked nodes:', clickedNodes);
});
});
}
$('#reload').click(function () {
// Cypher query に入力されたクエリを読み込んで再描画する
var cypher = $('#cypher').val();
if (cypher.length > 3) {
viz.renderWithCypher(cypher);
} else {
console.log('reload');
viz.reload();
}
});
$('#stabilize').click(function () {
// うねうねと動く描画を停止する
viz.stabilize();
});
</script>
</html>
4. まとめ
ここでは neovis.js 基本の設定方法を紹介しました。参考になれば幸いです。
4.1. 参考
以下参考にさせていただきました。ありがとうございます。