70
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

フロムスクラッチAdvent Calendar 2016

Day 16

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

Last updated at Posted at 2016-12-16

やったこと

  • データベース指定すると、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とこのスキーマしか使わないので直接こんな感じでいきます。

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を準備

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

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とかにしておきます。

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に!

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分くらいきれいにする自信がないのでタスクで!

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

あとがき

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

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

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

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

結論

メリクリまであと9日!

70
4
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
70
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?