Neo4jのデータベースサーバにはデフォルトでWebブラウザーというアドミン機能があり、ブラウザ内でログイン後にデータベース管理のクエリやデータ取得のクエリを実行することができます。そして、ノードデータのビジュアル表示にも対応しているので、ノード間の関連性を視覚的に理解することができます。しかし、これはNeo4jデータベースが提供している機能なのでこのデータのビジュアル表示を自分のWebアプリのプロジェクトにそのまま適用することができません。
そこで、Javascriptのライブラリを使用すれば自分のプロジェクトでグラフデータをビジュアルで表示することが可能になります。今回は以下のビジュアライゼーションツール2つを試してみることにします。Neo4jのneovis.jsはもうアクティブにメンテナンスはされていいので省略します。
- Vis-network.js
- Graphology + Sigma.js
事前準備
AuraDBにNeo4jのインスタンスを作成
今回はAuraDBのインスタンスを使用します。以下の記事を参考にAuraDBのインスタンスを作成し、かつ映画のサンプルデータを作成しておきましょう。

ApacheサーバとPHPのインストール
ApacheとPHPの環境を構築して、AuraDBのインスタンスから取得したグラフデータをブラウザに表示します。以下の記事を参考にしてApacheとPHPのセットアップを行ってください。


ComposerでLaudisのPHPドライバーをインストール
PHPからAuraDBのインスタンスに接続する場合、PHP用のドライバーが必要となります。以下の記事を参考にComposerのインストールとNeo4jのPHP用ドライバーをインストールまでを行ってください。

Graphology + Sigma.js
- Graphology
-
https://graphology.github.io
グラフデータの操作・編集用のライブラリで描画機能はない。 - sigma.js
-
https://www.sigmajs.org
Graphologyのデータを元にした描画するためのビジュラライゼーションライブラリ。
NPMコマンドのインストール
NPMコマンドを使用しますので、以下の記事を参考にNode.jsをインストールしてください。

graphologyのNodeパッケージをインストール
Nodeプロジェクトを作成し、graphologyとsigmaのパッケージをインストールしていきます。graphology-layout-forceatlas2パッケージのバンドル化にはViteを使用するので、Viteパッケージもインストールしておきます。
【Nodeプロジェクトのフォルダ作成と移動】
mkdir /var/www/sigma-graphology && cd /var/www/sigma-graphology
【プロジェクトの設定ファイルを作成】
npm init -y
【Viteパッケージのインストール】
npm install vite -l --save
【Graphologyパッケージのインストール】
// https://www.npmjs.com/package/graphology
npm install -l --save graphology
【Graphologyの追加ライブラリのインストール】
// https://www.npmjs.com/package/graphology-layout-forceatlas2
npm install -l --save graphology-layout-forceatlas2
【Sigmaパッケージのインストール】
// https://www.npmjs.com/package/sigma
npm install -l --save sigma
Viteの詳細については以下の記事を参考にしてください。

graphology-layoutのビルド用にViteの設定をする。
ビルドの作成
【プロジェクトフォルダに移動】
cd /var/www/sigma-graphology
【エントリーポイントのファイルの作成】
index-forceatlas2.js
> 以下内容
import forceAtlas2 from 'graphology-layout-forceatlas2';
export { forceAtlas2 };
Viteの設定ファイルの追加
vite.config.js
> 以下内容
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: 'index-forceatlas2.js',
name: 'GraphologyLayoutForceAtlas2',
fileName: () => 'graphology-layout-forceatlas2.umd.js',
formats: ['umd']
},
rollupOptions: {
external: [],
output: {
globals: {}
}
}
}
});
package.jsonファイルへの追記
package.jsonファイルに以下の様に”build”の行を追加してください。
package.json
> 以下内容
"scripts": {
"build": "vite build",
"test": "echo \"Error: no test specified\" && exit 1"
},
ビルドの実行
【ビルドコマンド】
npm run build
> dist/ディレクトリが作成され、その中にgraphology-layout-forceatlas2.umd.jsビルドファイルが作成されます。
【ファイルのコピー】
cp -ip dist/graphology-layout-forceatlas2.umd.js /var/www/html/.
その他必要なライブラリもコピー
以下のライブラリも公開ディレクトリにコピーします。
【graphologyライブラリ】
cp -ip node_modules/graphology/dist/graphology.umd.min.js /var/www/html/.
【sigmaライブラリ】
cp -ip node_modules/sigma/dist/sigma.min.js /var/www/html/.
サンプルコード
index.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once('../lib/vendor/autoload.php');
try {
//$connection_url = 'bolt+s://neo4j:パスワード@ホスト名';
//AuraDBのホスト名は以下の様なホスト名になります。
$connection_url = 'bolt+s://neo4j:パスワード@インスタンスID.databases.neo4j.io';
$client = Laudis\Neo4j\ClientBuilder::create()
->withDriver('bolt+s', $connection_url)
->build();
} catch(Exception $e) {
$err_msg = "Exception: ".$e->getLine()." : ".$e->getMessage();
var_dump($err_msg);
exit;
}
try {
$cypher = "";
$cypher .= " MATCH (p:Person)-[r]-(m:Movie {title: 'Cloud Atlas'}) ";
$cypher .= " RETURN p, r, m ";
$cypher .= " ; ";
$statement = new Laudis\Neo4j\Databags\Statement($cypher, []);
$result = $client->runStatement($statement);
} catch (Exception $e) {
$err_msg = "Exception: ".$e->getLine()." : ".$e->getMessage();
var_dump($err_msg);
exit;
}
$nodes = [];
$edges = [];
foreach ($result as $record) {
$p = $record->get('p');
$m = $record->get('m');
$r = $record->get('r');
foreach ([$p, $m] as $node) {
$id = $node->getId();
if (!isset($nodes[$id])) {
$props = $node->getProperties();
$label = $props['name'] ?? $props['title'] ?? 'Node';
$nodes[$id] = [
'key' => $id,
'label' => $label,
// ノード間の距離を縮めるため範囲を狭く
'x' => rand(0, 15) / 10,
'y' => rand(0, 15) / 10,
// ノードの縁を太くする
'size' => 30, //ノードの大きさ
'color' => '#888'
];
}
}
$edges[] = [
'key' => uniqid('e'),
'source' => $r->getStartNodeId(),
'target' => $r->getEndNodeId(),
'label' => $r->getType()
];
}
$data_json = json_encode(['nodes' => array_values($nodes), 'edges' => $edges]);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Graphology + Sigma.js with jQuery</title>
<!-- Graphology + Layout -->
<!-- copy from npm dist/ -->
<script src="/graphology.umd.min.js"></script>
<!-- bundled by vite -->
<script src="/graphology-layout-forceatlas2.umd.js"></script>
<!-- Sigma.js -->
<!-- copy from npm dist/ -->
<script src="/sigma.min.js"></script>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
#container {
margin-left: auto;
margin-right: auto;
width: 800px;
height: 600px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
const graph = new graphology.Graph();
const data = JSON.parse('<?php echo addslashes($data_json); ?>');
data.nodes.forEach(node => {
graph.addNode(node.key, {
label: node.label,
x: node.x,
y: node.y,
size: node.size,
color: node.color
});
});
data.edges.forEach(edge => {
graph.addEdge(edge.source, edge.target, {
label: edge.label
});
});
const settings = GraphologyLayoutForceAtlas2.forceAtlas2.inferSettings(graph);
GraphologyLayoutForceAtlas2.forceAtlas2.assign(graph, {
settings,
iterations: 100
});
const renderer = new Sigma(graph, document.getElementById('container'));
// 初期ズームを半分に設定
/*
ratio: 1 → 標準スケール
ratio: 0.5 → ズームイン
ratio: 2 こっちがズームアウトっぽい
*/
renderer.getCamera().setState({
ratio: 2
});
</script>
</body>
</html>
表示結果
表示はカスタマイズできますが、ここでは必要最低限に留めています。

vis-network.js (旧vis.js)
サイトURL
GitHub URL
https://github.com/visjs/vis-network
サンプルコード
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once('../lib/vendor/autoload.php');
try {
//$connection_url = 'bolt+s://neo4j:パスワード@ホスト名';
//AuraDBのホスト名は以下の様なホスト名になります。
$connection_url = 'bolt+s://neo4j:パスワード@インスタンスID.databases.neo4j.io';
$client = Laudis\Neo4j\ClientBuilder::create()
->withDriver('bolt+s', $connection_url)
->build();
} catch(Exception $e) {
$err_msg = "Exception: ".$e->getLine()." : ".$e->getMessage();
var_dump($err_msg);
exit;
}
try {
$cypher = "";
$cypher .= " MATCH (p:Person)-[r]-(m:Movie {title: 'Cloud Atlas'}) ";
$cypher .= " RETURN p, r, m ";
$cypher .= " ; ";
$statement = new Laudis\Neo4j\Databags\Statement($cypher, []);
$result = $client->runStatement($statement);
} catch (Exception $e) {
$err_msg = "Exception: ".$e->getLine()." : ".$e->getMessage();
var_dump($err_msg);
exit;
}
$nodes = [];
$edges = [];
foreach ($result as $record) {
$p = $record->get('p');
$m = $record->get('m');
$r = $record->get('r');
foreach ([$p, $m] as $node) {
$id = (string)$node->getId(); //文字列にキャスト
if (!isset($nodes[$id])) {
$props = $node->getProperties();
$label = $props['name'] ?? $props['title'] ?? 'Node';
$nodes[$id] = [
'id' => $id,
'label' => $label
];
}
}
$edges[] = [
'id' => uniqid('e'),
'from' => (string)$r->getStartNodeId(),
'to' => (string)$r->getEndNodeId(),
'label' => $r->getType()
];
}
$data_json = json_encode(['nodes' => array_values($nodes), 'edges' => $edges]);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Network</title>
<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
<style type="text/css">
#mynetwork {
margin-left: auto;
margin-right: auto;
width: 800px;
height: 600px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="mynetwork"></div>
<script>
const response = JSON.parse('<?php echo addslashes($data_json); ?>');
var nodes = new vis.DataSet(response.nodes);
var edges = new vis.DataSet(response.edges);
var container = document.getElementById("mynetwork");
var data = {
nodes: nodes,
edges: edges
};
//ノードの横長を円に修正する
var options = {
nodes: {
shape: "dot", // 正円になる
size: 20, // サイズを調整
color: {
background: '#97C2FC',
border: '#2B7CE9'
},
font: {
size: 16,
color: '#000'
}
},
edges: {
arrows: {
to: { enabled: true, scaleFactor: 1 }
},
font: {
align: "middle"
},
smooth: true
},
physics: {
stabilization: true
}
};
new vis.Network(container, data, options);
</script>
</body>
</html>
表示結果
こちらも表示はカスタマイズできますが、ここでは必要最低限に留めています。

まとめ
今回はNeo4jノーグラフデータを元に、そのビジュアライゼーションライブラリを2つ試しました。グラフデータはNeo4jのクラウドサービスであるAuraDBからDBのインスタンスを作成してデータを取得しました。そして、ApacheとPHPの環境を用意して、Neo4j用のPHPライブラリもインストールしました。Graphplogy+Sigma.jsを試す際にはNPMパッケージを利用しましたね。加えてViteもインストールしてパッケージのバンドルファイルも作成しました。同様にvis-network.jsでもビジュアル表示を試しました。表示用のカスタマイズオプションは沢山あるので自分でも色々と試してみましょう。