Neo4jとCypherに入門して使いどころを考えてみた


# 前提
* 作業マシンにインストールして使ってみる
* 作業マシンはOS X El Capitan(バージョン10.11.4)


# install
## brewで簡単に入る
```
brew install neo4j
```

## Javaが必要なので入ってなければ入れる
```
brew cask install java
```

# 起動
```
➜  ~ neo4j start
Unable to find any JVMs matching version "1.7".
Starting Neo4j Server...WARNING: not changing user
process [57371]... waiting for server to be ready............... OK.
http://localhost:7474/ is ready.
```
`http://localhost:7474/`ブラウザで管理画面みたいなのにアクセスできる
最初にID,PW入力を求められるが最初は`neo4j`,`neo4j`でログインできる

# neo4jのディレクトリ構成
/usr/local/Cellar/neo4j/2.3.2

```
├── bin   ・・・起動系スクリプト
│   ├── neo4j -> ../libexec/bin/neo4j
│   ├── neo4j-import -> ../libexec/bin/neo4j-import
│   └── neo4j-shell -> ../libexec/bin/neo4j-shell
└── libexec
    ├── bin　・・・起動系スクリプト
    │   ├── neo4j
    │   ├── neo4j-import
    │   ├── neo4j-installer
    │   ├── neo4j-shell
    │   └── utils
    ├── conf　・・・設定系ディレクトリ
    │   ├── neo4j-http-logging.xml
    │   ├── neo4j-server.properties
    │   ├── neo4j-wrapper.conf
    │   ├── neo4j.properties
    │   └── ssl
    │       ├── snakeoil.cert
    │       └── snakeoil.key
    ├── data　・・・実データやlogが出力されるディレクトリ
    │   ├── dbms
    │   │   └── auth
    │   ├── graph.db
    │   │   └── store_lock
    │   ├── log
    │   │   └── console.log
    │   └── neo4j-service.pid
    ├── lib　・・・coreライブラリ群
    ├── plugins　・・・プラグイン
    │   └── README.txt
    └── system　・・・neo4jのコア
```
まぁ各ディレクトリにREADMEあるので読めば詳しくわかります


# 試しのテストデータの準備
* 異なるジャンルのお店が２つあり、それぞれの商品を一般消費者が購入することをイメージ
* データ構造は下記

```
├── category1
│   └── shop1
│       ├── item1
│       └── item2
└── category2
    └── shop2
        └── item3
```
* `category1`という食品カテゴリには`shop1`があり、商品`item1(9500円)`,`item2（1200円）`がある
* `category1`という雑貨カテゴリには`shop2`があり、商品`item3（15000円）`がある

```
─── users
    ├── user1
    ├── user2
    ├── user3
    └── user4
```
* 消費者が4人いる
* `user1`は`item1`を購入
* `user2`も`item1`を購入
* `user3`は`item2`を購入
* `user4`は`item3`を購入


# 上記のデータをCSVで用意


```users.csv
user_id:ID,name,email,age:int,industry,gender,:LABEL
user1,user_name1,1@example.com,37,サービス業,male,user
user2,user_name2,2@example.com,17,ネットサービス,male,user
user3,user_name3,3@example.com,27,公務員,female,user
user4,user_name4,4@example.com,44,公務員,male,user
```

```orders.csv
order_id:ID,user_id,shop_id,category_id,price,item_id,:LABEL
order1,user1,shop1,cate1,9500,item1,order
order2,user2,shop1,cate1,9500,item1,order
order3,user3,shop1,cate1,1200,item2,order
order4,user4,shop2,cate2,15000,item3,order
```

```items.csv
tem_id:ID,name,shop_id,:LABEL
item1,商品A,shop1,item
item2,商品B,shop1,item
item3,商品C,shop2,item
```


```shops.csv
shop_id:ID,name,category_id,:LABEL
shop1,ショップA,cate1,shop
shop2,ショップB,cate2,shop
```


```category.csv
category_id:ID,name,:LABEL
cate1,カテゴリ1,category
cate2,カテゴリ2,category
```

# データの投入
CSVからデータ投入は２種類ある

## LOAD CSV
* 既存のDBに対してノードや関係性の追加や削除、属性の追加、削除、更新をする
* Javaのヒープメモリを気にする必要がある

```
LOAD CSV FROM 'file:///path/users.csv' AS line
CREATE (:user { email:line[1], gender:line[2], age:toInt(line[3]), industry:line[4] })
```

## neo4j-importコマンド
* 新規のDBに対してノードや関係性の追加や削除、属性の追加、削除、更新をする
* Javaのヒープメモリを気にしなくて良い
* 今回はこっちを利用

```
➜  neo4j neo4j-import --into /usr/local/Cellar/neo4j/2.3.2/libexec/data/graph2.db --nodes category.csv --nodes items.csv --nodes orders.csv --nodes shops.csv --nodes users.csv
Unable to find any JVMs matching version "1.7".
Importing the contents of these files into /usr/local/Cellar/neo4j/2.3.2/libexec/data/graph2.db:
Nodes:
  /Users/a12091/neo4j/category.csv

  /Users/a12091/neo4j/items.csv

  /Users/a12091/neo4j/orders.csv

  /Users/a12091/neo4j/shops.csv

  /Users/a12091/neo4j/users.csv

Available memory:
  Free machine memory: 17.46 MB
  Max heap memory : 1.78 GB

Nodes
[*>:??--------------------------------------------------------------------------------------|||] 10k
Done in 722ms
Prepare node index
[*DETECT:7.63 MB-------------------------------------------------------------------------------]   0
Done in 22ms
Calculate dense nodes
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 10ms
Relationships
[*>:??-----------------------------------------------------------------------------------------]   0
Done in
Node --> Relationship
[*v:??-----------------------------------------------------------------------------------------] 10k
Done in 12ms
Relationship --> Relationship
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 11ms
Node counts
[>|*COUNT:76.29 MB-----------------------------------------------------------------------------] 10k
Done in 76ms
Relationship counts
[*>:??-----------------------------------------------------------------------------------------]   0
Done in

IMPORT DONE in 2s 354ms. Imported:
  15 nodes
  0 relationships
  67 properties
```
入った

# 続いてRelationships設定
* RelationshipsようのCSVを用意すれば、そのまま投入できるが、ここでは基本データはCSVでCypherからRelationships設定という流れにする


## ユーザと購入を紐付け
```
MATCH (o:order),(u:user) 
WHERE o.user_id = u.user_id 
CREATE (u)-[:BUY]->(o);
```

## 購入データと商品を紐付け
```
MATCH (o:order),(i:item) 
WHERE o.item_id = i.item_id 
CREATE (o)-[:ORDER]->(i);
```

## お店と商品を紐付け
```
MATCH (s:shop),(i:item)
WHERE s.shop_id = i.shop_id
CREATE (i)-[:PART]->(s);
```

## お店とお店のジャンルを紐付け
```
MATCH (c:category),(s:shop)
WHERE c.category_id = s.category_id
CREATE (s)-[:CATE]->(c);
```

# 管理画面から見てみる
![Neo4j.png](https://qiita-image-store.s3.amazonaws.com/0/106937/3680ab58-5184-78ca-fc5d-febe9aa9c800.png)
* 色の設定とかはしてあげると綺麗に見える

## 注意
* 管理画面からは参照先のDBの変更はできない様子
* `neo4j/2.3.2/libexec/conf/neo4j-server.properties`の参照DBを変えてる必要あり
 * 変更後は`neo4j restart`で

# データを操作する - Cypher QL - 
* CypherはNeo4jのグラフ構造のデータ処理を扱う際に使うSQLライクなQuery Language

## 実行ツール
### Neo4jウェブインターフェース
Neo4j標準でついている。WEBから実行可能(http://localhost:7474/)

### Neo4jシェル
neo4j-shellで起動してコンソールからCypherを流して使う

### API経由
REST API

### Neo4jドライバー
主要な各言語からアクセスして操作できる

## 基本（取得）
### すべてのノードを取得
```
MATCH (n) RETURN n
```

### すべてのノードとrelashonship取得
```
MATCH (n -[r]-> m) RETURN n,r,m;
```

## 削除
### すべてのノードを削除
```
MATCH n DELETE n;
```

### すべてのノードとrelashonshipを削除
```
MATCH (n -[r]-> m) DELETE n,r,m;
```

### 削除裏技(data配下をrm)
```
/usr/local/Cellar/neo4j/2.3.2/libexec/data
```

# ここからNeo4j(Graph DB)を活用しているメリット
## Graph Algorithms
下記のコンポーネントが含まれてる

* Shortest paths
* all paths
* all simple paths
* Dijkstra and
* A*

ひとつひとつをしっかり見ると深すぎるので、分かりやすい`Shortest paths`を例に扱って見る

## Shortest paths = 最短経路
### ex1
`user_name1`と`user_name3`は今は友達ではないが、友達にさせるにはどんな共通項目があるかと調べる


#### コンソールから
```
neo4j-sh (?)$ MATCH p=shortestPath(
>   (a:user {name:"user_name1"})-[*]-(b:user {name:"user_name3"})
> )
> RETURN p;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| p                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [Node[11]{user_id:"user1",name:"user_name1",email:"1@example.com",age:37,industry:"サービス業",gender:"male"},:BUY[0]{},Node[5]{order_id:"order1",user_id:"user1",shop_id:"shop1",category_id:"cate1",price:"9500",item_id:"item1"},:ORDER[4]{},Node[2]{item_id:"item1",name:"商品A",shop_id:"shop1"},:PART[8]{},Node[9]{shop_id:"shop1",name:"ショップA",category_id:"cate1"},:PART[9]{},Node[3]{item_id:"item2",name:"商品B",shop_id:"shop1"},:ORDER[6]{},Node[7]{order_id:"order3",user_id:"user3",shop_id:"shop1",category_id:"cate1",price:"1200",item_id:"item2"},:BUY[2]{},Node[13]{user_id:"user3",name:"user_name3",email:"3@example.com",age:27,industry:"公務員",gender:"female"}] |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row
18 ms
```

#### 分かりにくいので管理画面から
![Neo4j.png](https://qiita-image-store.s3.amazonaws.com/0/106937/7ff12dd4-b10d-0725-ce62-39df78c8a846.png)

* ショップAという店で商品を購入していることが共有項目となり、結び付けることができる

### ex2
`user_name1`と`user_name2`は今は友達ではないが、友達にさせるにはどんな共通項目があるかと調べる

#### 管理画面から
![Neo4j.png](https://qiita-image-store.s3.amazonaws.com/0/106937/a54c70f4-90f0-b3aa-264c-058a4612cad5.png)

* 今度は商品Aという同じものを購入していることが共有項目となり、かつより近いところでつながり、結び助けることができる



## RDBだったら面倒なSQLがCypherなら超楽
### ex1
ショップAで男性購入者に人気の商品は(購入回数の多いのは)何か？を抽出しようとした時

#### mysqlの場合
* RDBの設計によるが大まかこんな感じになる。tableをjoinしまくってselectする
* またはsqlとプログラムを組み合わせて抽出する

```
select * 
from 
  購入Table 
left join 商品Table 
  on 購入Table.item_id = 商品Table.id
left join ユーザTable 
  on 購入Table.user_id = ユーザTable.id
left join ショップTable 
  on 購入Table.shop_id = ショップTable.id
where
 ユーザTable.gender = 'male'
AND
 ショップTable.name = 'ショップA'
```

#### Cypherの場合
```
MATCH (u:user {gender: "male"})-->(o:order)-->(i:item)-->(s:shop) 
WHERE s.name = "ショップA"
RETURN i.name 
```

# まとめ、使いどころ、雑感
* Graph Algorithmsを活用したSNS的なサービスの提供が可能（まぁみんなそう言ってる）
 * 〇〇の友達がいいねと言っています。みたいなやつ
 * 〇〇さんは友達ではないでしょうか？みたいなやつ
* Graph Algorithmsを活用したサービスの分析（CV分析）が可能
 * 何かプロダクトがあり、様々なログをFluentあたりで収集して、Neo4jに突っ込む。
 * プロダクトの会員データもどこかのタイミングでNeo4jに突っ込む
 * あとはCypherをフル活用して分析してプロダクトへ反映する
* RDBだったら複雑なSQLで計算量も時間もかかっていたのが、何分の１かで実現できるので良い感じのリコメンド機能とか割と簡単にできそう
 * どの商品にはどの属性のユーザが多く購入をする的なデータはすぐ計算・抽出する
 * 抽出したデータをもとにレコメンドようのデータKVSに突っ込む
 * ユーザの属性情報に基づいたレコメンドをKVSから取って表示してあげる
* まぁレコメンドはまたいろんなアルゴリズムが存在するし、一筋縄ではいかないよなぁ。。。
* Cypherをあまり詳しく記載しなかったが、http://neo4j.com/docs/stable/index.html を見ると面白そうなのがいっぱいある。Cypherの学習は面白そう
* データ分析ならぬデータ遊びで色々な傾向のデータが出せそうなきがする
* まぁ使いこなせればデータ分析系には強い武器になると思う
* ある程度つかんできたら、プロダクションのデータを一部持ってきて触った方が、よりリアリティがあるし、楽しくできると思う