neo4jでER図！コントローラとの関係も追加できるよツール(案!!かつmysql-rails)

# やったこと
* データベース指定すると、rakeタスクでER図作るようにした
* コントローラ名と使ってるテーブル指定すると、rakeタスクで関係追加できるようにした
* ただし、テーブル減った時とか考慮してないパターンあります。
* view作るとこは今回やってません。処理だけ。railsの勉強がてらな感じです。  
なのでコードはぐちゃってます。。。
* テーブルのカラムも属性に入れてますのでneo4j上本当にER図っぽくみれます。  
けど、紐付けはテーブルでしてしまった。。急ぎ過ぎました。

# mixの苦悩
neo4jはチュートリアルもやったし、暇つぶしに社員情報突っ込んでみたりはしたことある。
けどあまり活用できるアイディアがない。

qiitaの情報抜いて憧れの人との最短経路を！、、、
→なんか面白くなさそう。。。

ER図は？railsとmysqlのよくある構成なら
なんとなくいい感じで関係性や影響範囲可視化できそうでは！、、、
→うむ、よくわからん、一人ブレストじゃ何がいいかわからん。。。

# mixは苦悩を同僚にぶつけた
頼れる同僚にきいてみた

mix 「ねーねーneo4jでER図とか自動生成してみれたら便利っぽくないですか？
　　　今日qiita書く日なんでなのでやってみた結果を遊んだよ的な記事にして書こうかと(｡･ε･｡)」

@MasahiroSato 「うーん、workbenchとかそういう機能あるんじゃないっけ？」

mix 「。。。そこにrailsのコントローラとか(手動で)登録できたらどうです？(｡･ε･｡)」

@MasahiroSato 「それいいね！影響範囲わかるんだよね？いいじゃん★」

やった！ちょっとコントローラの情報を自動でとれるっぽくいっちゃった気がしなくもないけど
仮説検証完了だ！！

ということで今から作ってみます。

# やってみよう！

## 見切り発車で書いてみる

ruby on railsでいつも通りプロジェクト作ります。
名前はなんでもOK！

### neo4j操作用ライブラリ追加

```:Gemfile
gem 'neography'
```

```
bundle
```

#### テーブル準備(検証用)
いったんテーブルを作って、
後からinformation_schemaに接続するアプリにします。

既存があれば不要ですが、
動作確認のためサンプルでモデルとか外部キーで作っておきます。
- 会社←部署←チーム←メンバー(←はbelongs_toです)
- Mixという関係ないのも作っておきます。

```
rails g model Company name
rails g model Division name company:references
rails g model Team name division:references
rails g model Member name team:references
rails g model Mix name 
rake db:migrate
```

### アプリ接続先変更
外部キーとかテーブル一覧取るのは、
デフォルトではinformation_schemaじゃないと面倒なので、
mysqlの接続先変えます。
接続先変更はestablish_connection使って初めてのマルチデータベース、
とか思いましたが、neo4jとこのスキーマしか使わないので直接こんな感じでいきます。

```yaml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock
  database: information_schema #ここ！
```

どの環境でもinformation_schemaはそのまま前提、
ユーザーはinformation_schema見れるユーザにしてください。

#### マイグレーション使っちゃったから後片付け(検証用)
使わなきゃよかったです。
適当に書いちゃったので、片付け必要です。
無理やり消します。

```
rm    db/migrate/*.rb
rm    app/models/company.rb
rm    test/models/company_test.rb
rm    test/fixtures/companies.yml
rm    app/models/division.rb
rm    test/models/division_test.rb
rm    test/fixtures/divisions.yml
rm    app/models/team.rb
rm    test/models/team_test.rb
rm    test/fixtures/teams.yml
rm    app/models/member.rb
rm    test/models/member_test.rb
rm    test/fixtures/members.yml
rm    app/models/mix.rb
rm    test/models/mix_test.rb
rm    test/fixtures/mixes.yml
```

### modelを準備
外部キーとか見れるテーブルはココみたいでしたので
外部キー取得用に作成。

```rb:app/models/key_col_usage.rb
class KeyColUsage < ApplicationRecord
  self.primary_key = nil
  self.table_name = 'key_column_usage'
  default_scope {
    select(:referenced_table_name)
        .where('referenced_table_name IS NOT NULL')
  }
end
```

全テーブル、全カラムを取得するテーブルはココみたいでしたので、
テーブルとカラム取得用に作成。
columnという名前だと怪しいので適当にColumnInfoとかにしておきます。

```rb:app/models/column_info.rb
class ColumnInfo < ApplicationRecord
  self.primary_key = nil
  self.table_name = 'columns'
  default_scope {
    select(:table_name, :column_name, :ordinal_position)
  }
  scope :database, -> (database_name){where('table_schema = ?', database_name)}

  def to_h
    {ordinal_position => column_name}
  end
end
```

* primary_keyは複合キーで指定もできますが、  
今回見るだけなのでnilにします。
* default_scopeは無闇に設定しない方が良かったりしますが、  
用途が限定できるので設定します。

### taskを作ります
適当感満載になってきましたね。
GUI作っても良かったですがライブコーディングのつもりで
適当にやりました。

```
rails g task neo4j
```

### ER図作成タスクを作ります
まずはデータベース情報もらってER図をneo4jに！

```rb:lib/tasks/neo4j.rake
namespace :neo4j do
  desc 'create nodes and relations'
  task :load_from_db, ['database_name'] => :environment do |task, args|
    database_name = args.database_name
    # task共通だから外だししてもOK、改善案としてserviceを作ったりするべきかもしれません。
    require 'neography'
    neo = Neography::Rest.new({:authentication => 'basic', :username => 'neo4j', :password => '#パスワードだよ'})

    col_info_group_by_table = ColumnInfo.all.database(database_name).group_by { |col| col.table_name }
    
    # 全テーブルと全カラムをノードとして登録
    col_info_group_by_table.map { |table, cols| [\
          table, \
          cols.map { |col| col.to_h }.reduce({}) { |h, c| h.merge(c) }\
        ] }.to_h\
        .each do |table, cols_h|

      node = neo.find_nodes_labeled('table', table_name: table)[0] || neo.create_node(table_name: table)
      neo.add_label(node, 'table')
      neo.set_node_properties(node, cols_h)
    end

    # 登録したノード間のリレーションを設定(メソッド分けるべきですね、みにくい)
    col_info_group_by_table.each_key do |table|
      ar_list = KeyColUsage.where(table_name: table)
      unless ar_list.empty?
        node = neo.find_nodes_labeled('table', table_name: table)[0]
        ar_list.each do |keyColUsage|
          target_node = neo.find_nodes_labeled('table', table_name: keyColUsage.referenced_table_name)[0]
          # relationの存在確認はよくわからなかった
          neo.create_relationship(:belong_to, node, target_node)
        end
      end
    end
end
```

### コントローラも追加するタスクをつくります

GUIにしたり、解析したりするべきですよね。
GUIも解析も30分くらいきれいにする自信がないのでタスクで！

```rb:lib/tasks/neo4j.rake
 desc 'add controller そして使うテーブルも入れてね'
  task :add_controller_info, ['controller', 'table_name'] => :environment do |task, args|
    controller = args.controller
    table_name = args.table_name
    # やっぱり外だしすべきですね。
    require 'neography'
    neo = Neography::Rest.new({:authentication => 'basic', :username => 'neo4j', :password => '#パスワードだよ'})

    # コントローラのノードを作成
    node = neo.find_nodes_labeled('controller', controller_name: controller)[0] || neo.create_node(controller_name: controller)
    neo.add_label(node, 'controller')

    # コントローラとテーブルのノードのリレーションを作ります。
    target_node = neo.find_nodes_labeled('table', table_name: table_name)
    # relationの存在確認はよくわからなかった
    neo.create_relationship(:use, node, target_node)

  end
```

よし、もうこれでいい！

## rakeタスク実行してみます。

```
rake neo4j:load_from_db['#データベース名入れてね']
rake neo4j:add_controller_info['SampleController','mixes']
```

いけた！！

![graph.png](https://qiita-image-store.s3.amazonaws.com/0/70346/a0f110b8-1781-3ac3-bf36-4dd43c0e2335.png)

# あとがき
リレーションの存在判定は適当に動作確認してみたところ
ちょっと意図通りに行かないので外しました。
そのため、同じ入力を何度もするとリレーションが何重にもなります。
全取得する場合は、一度全消し。

もうちょっと手を入れたら実用化できるかも、という感じ。
コントローラ追加、編集のGUIがキモかもしれません。

新しいライブラリ使うときみなさんどうしているのでしょう。
rubyだと返却される型がわからず戸惑わないですか？慣れの問題？
docなかったりしたとき結構膝にくる。

javaだったら簡単に処理できる部分も
rubyだとどうやればいいのかわからないことが多い、勉強不足、コーディング不足、
反省。
わからないことは書き溜めて、理解したらまとめて記事とかにします。

# 結論
__メリクリまであと9日！__