LoginSignup
2
1

More than 1 year has passed since last update.

Flask + neovis.js で Neo4j のグラフデータを可視化する

Last updated at Posted at 2023-03-12

1. はじめに

 何周回遅れかわからないですが、最近 NoSQL (Mongo, Neo4j) にはまっています!
 グラフ型 NoSQL データベース Neo4j の勉強を始めて、ちょっと可視化アプリでも作ってみるかと、Python の Flask と 以下の neovis.js を使って作成しようとしたのですが、

neovis.js がメジャーバージョンアップ (v2.0) したらしく、色々と設定項目が変わって Web で使い方を調べるのに苦労したので、ここで紹介します。

先に結果だけ貼ると、Neo4j にある movie データサンプルを neovis.js を使って可視化したものが以下になります(オレンジが映画、紫は人名です)。

neovis02.png

このページでは neovis.js を使って Neo4j のデータを可視化する方法を中心に紹介します。Flask を使ったアプリケーションの作りこみについては行っていません。

2. Neo4j のセットアップ

 Docker を使ったNeo4j のセットアップ方法を紹介します。すでに Neo4j の実行環境がある方は本章は飛ばしてください。

以下の docker-compose.yml ファイルを準備し、

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

play_movies.png

と実行し、表示される Chpher スクリプトを叩いて、movie データセットを作成してください。

$ MATCH (n) RETURN n

で以下のようなグラフが確認できます。

graph.png

3.2. Flask + neovis.js での基本の描画

 以下のコマンドで、Python 環境に Flask をインストールします。

$ pip install flask

Flask 用の アプリケーションファイルを準備し、

app.py
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 フォルダに作成します。

templates/index.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: 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 

play_movies_sample.png

上記図では、ノードの重なりが多いなと感じると思いますが、あとで改善する設定を紹介します。

Cypher query の入力ボックスから、描画を変更することができ、例えばトムハンクスだけを表示するようにすると

MATCH (p:Person {name: "Tom Hanks"})-[r]->(m:Movie) RETURN *

以下のようになります。

play_movies_sample2.png

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 の線名を追加すると下記のようになりました。

play_movies_sample3.png

3.3.4. ノードの色とブロックの形の変更

 公式 のREADME にあるように、あるラベルのあるプロパティに応じて色を変えたいときは、以下のように group にそのプロパティ名を設定します。

labels: {
    Character: {
        label: "name",
        group: "community",
    }
}

一方で、今回は label の種類ごと(Person, Movieなど)に色を指定したかったので、その場合は以下のように [NeoVis.NEOVIS_ADVANCED_CONFIG]static.color に色コードを指定します(colorred, 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 にカーソルをあてると以下のように表示されます。

play_movies_sample4.png

3.3.6. ノードの 配置

 ノードの配置をツリー構造にしたいときは,visConfig に以下のような layout を追加します。

visConfig: {
    layout: {
        hierarchical: {
            enabled: true,
            direction: "LR",
            sortMethod: "directed",
            levelSeparation: 300  //階層間の距離(ちょっと広くする)
        }
    },
},

play_movies_sample5.png

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 については入れた方がさらに重ならない気がするのですが、実行時にかなりうねうねとするのでコメントアウトしています。

変更前と変更後で比較を行ったものか以下になります。

初期設定

before

変更後

after

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 ファイルを以下に貼っておきます。

templates/index.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. 参考

以下参考にさせていただきました。ありがとうございます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1