はじめに
グラフデータベースNeo4jと、図表化JavascriptライブラリCytoscapeを触ってみた時のメモ。
組み合わせた情報で、コードレベルの情報がほとんどないので、誰かの参考になれば嬉しい。
(実際組み合わせてみると、それ単独では記事化するほどの内容ではなかったけど・・・)
Neo4j
インストール
Docker
ほぼこれ
$ docker pull neo4j
$ docker run \
--detach \
--publish=7474:7474 --publish=7687:7687 \
--ulimit=nofile=40000:40000 \
neo4j
初期設定
初期画面アクセス時に聞かれたこと ※初期ユーザー(neo4j/neo4j)でログインすると、パスワード変更を求められる。
以降実際とは異なるID/Passwordで記載します。
ID:neo4j
Pass:password
やっぱdockerは環境構築が楽だなぁ・・・
使ってみた
ほぼこれ
ノードの作成/参照/削除
Taroを作る
CREATE (:Person{name:"Taro", age:20});
実行結果が見れる。
1ラベル追加(Personのことか)、1ノード作成(Personラベルに1ノード追加したということだな)、2プロパティ設定(nameとageを設定したってことだろう)、528msで完了。
Codeってタブは別に面白い話無いっぽいな。
Taroを取得する
MATCH (p:Person{name:"Taro"}) RETURN p;
おぉ!Taroが出てきた!
テーブルは、想定通り非構造データ。キーとかどうなってるんだ?もっかいTaro追加するとどうなるんだろう?
もう一度Taroを作って、取得する
CREATE (:Person{name:"Taro", age:20});
MATCH (p:Person{name:"Taro"}) RETURN p;
Taroが二人!キーとかは無いのか???自動生成って理解したほうが良さそうだなー(ドキュメント読めよって言われればそれまでだけど・・・)
全てのTaroを消す
MATCH (p:Person{name:"Taro"}) DELETE p;
Taroを消した。予想通り2ノード消えている。
ちゃんと消えてるか確認する
MATCH (p:Person{name:"Taro"}) RETURN p;
消したので、誰も居ない。
リレーションの作成/参照
ひとまず、A/B/Cの3人を作る
CREATE
(:Person{name:"Alice", age:20}),
(:Person{name:"Bob", age:22}),
(:Person{name:"Carol", age:23});
A/B/Cを作る。できたっぽい。
ちゃんと作れているか確認
MATCH (n) RETURN n;
全ノード取得は、こうやって書くらしい。今更だけど、このSQLっぽいやつは、Cypherと呼ぶらしい。
リレーションの作成
MATCH (p1:Person{name:"Alice"}), (p2:Person{name:"Bob"}) CREATE (p1)-[:Friend]->(p2) ;
関係性の追加はこうするらしい。Carolが寂しそう。
ちなみに、触って動かせる。二人の関係を邪魔することはできない。
よく見たら、Ageとか分からんやんけ、って思った。けど、選択すると、画面下部に詳細情報が表示されるらしい。
ID採番されているやんけ。(なぜCarolは40?ちなみに、Aliceが0、Bobが20だった。20ずつ増える???)
どの属性を表示させるかは選べるらしい。(まぁそうだよね。)ただ、全部は表示できず、どれか一つだけっぽい。
Cytoscape
Cytoscapeを選んだ理由
slideshare - (2017.6.9) Neo4jの可視化ライブラリまとめ
Cytoscapeってのが良さそう。
Cytoscape.js単独での実装
Qiita - Cytoscape.jsを用いてデータを可視化する
※上記を参考にちょっと手直し(jQueryのライブラリを最新化したり、gridを文字列化したりしてる)
最終的にはNeo4jにつなげる必要があるけど、ひとまずは素の状態で触ってみる。
- 丸いヤツとか、ドラッグアンドドロップできる。
- マウスのスクロールで拡大・縮小できる。
See the Pen Cytoscape_test1 by nh321 (@nh321) on CodePen.
まぁ要素直書きだし、そうだろうな、って感じ。
Neo4jをjavascriptで参照する
シンプルなNeo4j接続
個人ブログ(日本語)見るとnode.jsとかpythonでサーバサイド実装してるみたいだけど、やりたいのはクライアント(ブラウザ)実装。
(DBアクセスのためのID/Passが露出するので、本来はサーバサイドに書くべきだろうけど、今回はお試ししたいだけなので、気にしない。)
よく見ると、CDN経由でライブラリ取得できるみたいなので、それをつかう。
Github - neo4j/neo4j-javascript-driver
基本は、Githubのusageを参考に作成。
そのままだと動かなかったので、stackoverflowも参照。
参考:
<html>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/neo4j-driver"></script>
<script>
$(function(){
//https://stackoverflow.com/questions/44205354/unable-to-establish-a-neo4j-bolt-driver-connection-in-javascript
neo4j = neo4j.v1
//driveの設定
var driver = neo4j.driver(
'bolt://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
)
//セッションの生成
var session = driver.session()
// Run a Cypher statement, reading the result in a streaming manner as records arrive:
session
.run('MATCH (person:Person) RETURN person.name AS name')
.subscribe({
onNext: function(record) {
console.log(record.get('name'))
},
onCompleted: function() {
session.close()
driver.close()
},
onError: function(error) {
console.log(error)
}
})
});
</script>
</html>
そうすると、以下のようにF12でconsoleにログ出力されていることを確認。ちゃんと動いている。良かった。
グラフ情報として取得するのはどうやるんだ・・・???
Neo4jからリレーションデータを取得する
誰かのサンプルを見つけたので使ってみる。
- 参考のベースにしたもの
- Cypherを考えるときに参考にしたもの
Cypherの結論
MATCH (m)-[r]-(n) RETURN m.name as m,type(r) as type,startnode(r).name as start,n.name as n;
Javascripte側はたぶん以下で行けるはず。(sample読んだ雰囲気で理解)
record.get('m');
record.get('type');
record.get('start');
record.get('n');
以下、うごいたhtml/javascript。
<html>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/neo4j-driver"></script>
<script>
$(function(){
//https://stackoverflow.com/questions/44205354/unable-to-establish-a-neo4j-bolt-driver-connection-in-javascript
neo4j = neo4j.v1
//driveの設定
var driver = neo4j.driver(
'bolt://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
)
//セッションの生成
var session = driver.session()
// Run a Cypher statement, reading the result in a streaming manner as records arrive:
session
.run('MATCH (m)-[r]-(n) RETURN m.name as m,type(r) as type,startnode(r).name as start,n.name as n')
.subscribe({
onNext: function(record) {
console.log("---");
console.log(record.get('m'));
console.log(record.get('type'));
console.log(record.get('start'));
console.log(record.get('n'));
},
onCompleted: function() {
session.close()
driver.close()
},
onError: function(error) {
console.log(error)
}
})
});
</script>
</html>
Neo4jとCytoscape.jsと組み合わせる
わりとできた。(Cypherは微修正してる)
ソース動いたの確認してから、仲良し3人組に変更してみたけど、ソース修正しなくてもちゃんと動く。いい感じ。
<html>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/neo4j-driver"></script>
<script src="http://cdn.bootcss.com/cytoscape/2.3.16/cytoscape.min.js"></script>
<script>
$(function(){
//https://stackoverflow.com/questions/44205354/unable-to-establish-a-neo4j-bolt-driver-connection-in-javascript
neo4j = neo4j.v1
//driveの設定
var driver = neo4j.driver(
'bolt://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
)
//セッションの生成
var session = driver.session()
var style = [];
var elements = {
nodes:[],
edges:[]
};
// Run a Cypher statement, reading the result in a streaming manner as records arrive:
session
.run('MATCH (m:Person)-[r]->(n:Person) RETURN m.name as mname, ID(m) as mid, type(r) as type, n.name as nname, ID(n) as nid')
.subscribe({
onNext: function(record) {
console.log("---");
console.log(record.get('mname'));
console.log(record.get('mid').toString());
console.log(record.get('type'));
console.log(record.get('nname'));
console.log(record.get('nid').toString());
//内容要素はJSONオブジェクトである、サーバ側加工しフロントに渡すもの
elements.nodes.push({
data: {id: record.get('mid').toString(), name: record.get('mname'), label: 'Person'}
})
elements.nodes.push({
data: {id: record.get('nid').toString(), name: record.get('nname'), label: 'Person'}
})
elements.edges.push({
data: {source: record.get('mid').toString(), target: record.get('nid').toString(), relationship: record.get('type')}
})
//内容要素を表現するCSS
var style = [
//セレクターで拾いた内容要素が 指定したCSSを適用する
//ノードの中で、label属性は「Peson」のノードが青色で表示し、文字はname属性を表示する
{ selector: 'node[label = "Person"]',
css: {'background-color': '#6FB1FC', 'content': 'data(name)'}
},
//エッジ全体で、文字はrelationship属性を表示する、終了点での矢印は三角形にする
{ selector: 'edge',
css: {'content': 'data(relationship)', 'target-arrow-shape': 'triangle'}
}
]
//レイアウト設定
var layout = {
//グリッドレイアウトを適用する
name : "grid"
}
// Cytoscapeオブジェクト初期化。
var cy2 = cytoscape({
// containerがHTML内の「cy2」DOM要素に指定
container: document.getElementById('cy2'),
elements: elements,
style: style,
layout: layout,
});
},
onCompleted: function() {
session.close()
driver.close()
},
onError: function(error) {
console.log(error)
}
})
});
</script>
<style type="text/css">
<!--
/* cytoscape graph */
#cy2 {
height: 300px;
width: 400px;
background-color: #f9f9f9;
}
-->
</style>
<div id="cy2"></div>
</html>
最終的にこんな感じになりました。(以下は画像)
さいごに
グラフィカルなツールは触ってて楽しいですねー。
これを使ってジョブの先行後続とか見やすくしたら面白いかなーとか考えます。
以上。