neo4j をインストールしてから Cytoscape と組み合わせるまでの話

# はじめに
グラフデータベースNeo4jと、図表化JavascriptライブラリCytoscapeを触ってみた時のメモ。
組み合わせた情報で、コードレベルの情報がほとんどないので、誰かの参考になれば嬉しい。
（実際組み合わせてみると、それ単独では記事化するほどの内容ではなかったけど・・・）

# Neo4j
## インストール
### Docker
ほぼこれ

- [Qiita - Neo4j Docker版を動かしてみました](https://qiita.com/awk256/items/305fd52c881d8df69de1)

```bash:pull
$ docker pull neo4j
```

```bash:お試しだけなので、ホストにマウントはさせないように変更
$ docker run \
    --detach \
    --publish=7474:7474 --publish=7687:7687 \
    --ulimit=nofile=40000:40000 \
    neo4j
```

### 初期設定
初期画面アクセス時に聞かれたこと　※初期ユーザー(neo4j/neo4j)でログインすると、パスワード変更を求められる。

以降実際とは異なるID/Passwordで記載します。
ID:neo4j
Pass:password

こんな感じのUI
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/f2768afe-9194-ab11-7187-b83edb538243.png)

やっぱdockerは環境構築が楽だなぁ・・・

## 使ってみた
ほぼこれ

- [Qiita - Neo4jことはじめ](https://qiita.com/tikamoto/items/c3a1bba12e9b83aee42a)

### ノードの作成/参照/削除

**Taroを作る**

```
CREATE (:Person{name:"Taro", age:20});
```

実行結果が見れる。

1ラベル追加（Personのことか）、1ノード作成（Personラベルに1ノード追加したということだな）、2プロパティ設定（nameとageを設定したってことだろう）、528msで完了。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/fadebcbe-f364-f465-0852-14f303e721c4.png)

Codeってタブは別に面白い話無いっぽいな。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/a9aa9a00-b49a-5ad5-e2d7-7a2be150a10d.png)

**Taroを取得する**

```
MATCH (p:Person{name:"Taro"}) RETURN p;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/996da2d8-2e38-b872-587b-0c92f3cf40b5.png)

おぉ！Taroが出てきた！

テーブルは、想定通り非構造データ。キーとかどうなってるんだ？もっかいTaro追加するとどうなるんだろう？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/d6d95069-e8b1-53b8-dd36-47dd0e354739.png)

**もう一度Taroを作って、取得する**

```
CREATE (:Person{name:"Taro", age:20});
MATCH (p:Person{name:"Taro"}) RETURN p;
```

Taroが二人！キーとかは無いのか？？？自動生成って理解したほうが良さそうだなー（ドキュメント読めよって言われればそれまでだけど・・・）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/94821320-9783-2605-e93b-9143b70d8dd2.png)

**全てのTaroを消す**

```
MATCH (p:Person{name:"Taro"}) DELETE p;
```

Taroを消した。予想通り2ノード消えている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/b8fdf74a-6b7a-c198-ff89-d062426892df.png)

**ちゃんと消えてるか確認する**

```
MATCH (p:Person{name:"Taro"}) RETURN p;
```

消したので、誰も居ない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/839c7f84-ea0f-9bb9-57a4-b345686f20d3.png)


### リレーションの作成/参照

**ひとまず、A/B/Cの３人を作る**

```
CREATE
     (:Person{name:"Alice", age:20}),
     (:Person{name:"Bob", age:22}),
     (:Person{name:"Carol", age:23});
```

A/B/Cを作る。できたっぽい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/852d061d-492d-c30a-ca41-02f6cd8c8b06.png)

**ちゃんと作れているか確認**

```
MATCH (n) RETURN n;
```

全ノード取得は、こうやって書くらしい。今更だけど、このSQLっぽいやつは、Cypherと呼ぶらしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/498220ef-dfce-acc5-079f-272beaf60a38.png)

**リレーションの作成**

```
MATCH (p1:Person{name:"Alice"}), (p2:Person{name:"Bob"}) CREATE (p1)-[:Friend]->(p2) ;
```

関係性の追加はこうするらしい。Carolが寂しそう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/21555ff3-c677-329a-ed02-1ef5429410cd.png)


ちなみに、触って動かせる。二人の関係を邪魔することはできない。

![neo4j_gui.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/d9b1e4f8-4df4-8a49-837a-785261d9547b.gif)

よく見たら、Ageとか分からんやんけ、って思った。けど、選択すると、画面下部に詳細情報が表示されるらしい。

ID採番されているやんけ。（なぜCarolは40？ちなみに、Aliceが0、Bobが20だった。20ずつ増える？？？）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/be49c152-2f42-8849-e67f-431f9dacc9f1.png)


どの属性を表示させるかは選べるらしい。（まぁそうだよね。）ただ、全部は表示できず、どれか一つだけっぽい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/eb96e5e1-58d3-e324-a06e-465207252172.png)


# Cytoscape
## Cytoscapeを選んだ理由
[slideshare - (2017.6.9) Neo4jの可視化ライブラリまとめ](https://www.slideshare.net/mkiuchi4/201769-neo4j/16)

Cytoscapeってのが良さそう。

## Cytoscape.js単独での実装
[Qiita - Cytoscape.jsを用いてデータを可視化する](https://qiita.com/Wangxinyin/items/6965abdd369736e4073f)
※上記を参考にちょっと手直し（jQueryのライブラリを最新化したり、gridを文字列化したりしてる）

最終的にはNeo4jにつなげる必要があるけど、ひとまずは素の状態で触ってみる。

- 丸いヤツとか、ドラッグアンドドロップできる。
- マウスのスクロールで拡大・縮小できる。

<p class="codepen" data-height="500" data-theme-id="0" data-default-tab="result" data-user="nh321" data-slug-hash="yLBJKdm" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Cytoscape_test1">
  <span>See the Pen <a href="https://codepen.io/nh321/pen/yLBJKdm/">
  Cytoscape_test1</a> by nh321 (<a href="https://codepen.io/nh321">@nh321</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

まぁ要素直書きだし、そうだろうな、って感じ。

# Neo4jをjavascriptで参照する
## シンプルなNeo4j接続
個人ブログ（日本語）見るとnode.jsとかpythonでサーバサイド実装してるみたいだけど、やりたいのはクライアント（ブラウザ）実装。

（DBアクセスのためのID/Passが露出するので、本来はサーバサイドに書くべきだろうけど、今回はお試ししたいだけなので、気にしない。）

よく見ると、CDN経由でライブラリ取得できるみたいなので、それをつかう。

[Github - neo4j/neo4j-javascript-driver](https://github.com/neo4j/neo4j-javascript-driver)

基本は、Githubのusageを参考に作成。
そのままだと動かなかったので、stackoverflowも参照。

参考：

- [stackoverflow - Unable to establish a neo4j - bolt driver connection in javascript](https://stackoverflow.com/questions/44205354/unable-to-establish-a-neo4j-bolt-driver-connection-in-javascript)

```html:neo4jサーバはローカルにあるので、雑なhtmlだけ乗せておく。
<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にログ出力されていることを確認。ちゃんと動いている。良かった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/45b11ae2-7288-d2a2-8f2a-e4e8a4f47b14.png)

グラフ情報として取得するのはどうやるんだ・・・？？？

## Neo4jからリレーションデータを取得する
誰かのサンプルを見つけたので使ってみる。

- 参考のベースにしたもの
    - [Github - neo4j-examples/cytoscape-graph-viz-lotr](https://github.com/neo4j-examples/cytoscape-graph-viz-lotr)
- Cypherを考えるときに参考にしたもの
    - [stackoverflow - How to return relationship type with Neo4J's Cypher queries?
](https://stackoverflow.com/questions/31485802/how-to-return-relationship-type-with-neo4js-cypher-queries)
    - [stackoverflow - Neo4j Cypher Get Relationship Direction](https://stackoverflow.com/questions/11393662/neo4j-cypher-get-relationship-direction)

**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
<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
<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>
```

最終的にこんな感じになりました。（以下は画像）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214587/d068a537-706c-aaab-3915-413b6b04beba.png)

# さいごに
グラフィカルなツールは触ってて楽しいですねー。
これを使ってジョブの先行後続とか見やすくしたら面白いかなーとか考えます。

以上。