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

More than 1 year has passed since last update.


やったこと


  • データベース指定すると、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日!