More Better Nested Set

1,559 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,559
On SlideShare
0
From Embeds
0
Number of Embeds
51
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

More Better Nested Set

  1. 1. イケテルbetter nested set 研究会 藤岡岳之(xibbar)
  2. 2. アジェンダbetter nested setを使って組織図を書く手軽に書きたいんだ∼!
  3. 3. Sectionモデルを作成1better nested setをインストール
  4. 4. Sectionモデルを作成2migrationファイル モデルはSectionという名前にすることにして、使用 するカラムはnameだけに better nested setにはparent_id、lft、rgtとい う:integerなカラムが必要class CreateSections < ActiveRecord::Migration def self.up create_table :sections do ¦t¦ t.column :name, :string, :null=>false t.column :parent_id, :integer t.column :lft, :integer t.column :rgt, :integer end end def self.down drop_table :sections endend
  5. 5. Sectionモデルを作成3app/models/section.rbの中身 class Section < ActiveRecord::Base acts_as_nested_set end
  6. 6. データを作成 ツリーを作成してみる% ./script/runner Section.create(:name=>"日本Ruby会議")% ./script/runner Section.create(:name=>"東京Ruby会議").move_to_child_of Section.find_by_name("日本Ruby会議")% ./script/runner Section.create(:name=>"札幌Ruby会議").move_to_child_of Section.find_by_name("日本Ruby会議")% ./script/runner Section.create(:name=>"Akasaka.rb").move_to_child_of Section.find_by_name("東京Ruby会議")% ./script/runner Section.create(:name=>"Asakusa.rb").move_to_child_of Section.find_by_name("東京Ruby会議") DBの中身は # select * from sections; id ¦ name ¦ parent_id ¦ lft ¦ rgt ----+--------------+-----------+-----+----- 1 ¦ 日本Ruby会議 ¦ ¦ 1 ¦ 10 2 ¦ 東京Ruby会議 ¦ 1¦ 2¦ 7 3 ¦ 札幌Ruby会議 ¦ 1¦ 8¦ 9 4 ¦ Akasaka.rb ¦ 2¦ 3¦ 4 5 ¦ Asakusa.rb ¦ 2¦ 5¦ 6 (5 rows)
  7. 7. 自作プラグインを導入./script/plugin install http://xibbar.net/svn/rails/plugins/trunk/acts_as_section_map/
  8. 8. モデルに一行追加 class Section < ActiveRecord::Base acts_as_nested_set acts_as_section_map end※ acts_as_section_mapは  acts_as_nested_setの下に書くこと
  9. 9. コントローラー 1行追加class WelcomeController <ApplicationController def index @table2=Section.table2 endend
  10. 10. ビューindex.rhtml <% section_map(@table2) do ¦table¦ %> <%=table.name%> <% end %> ※ テーブルの中身はブロックで指定 ※ プラグインの魔法で、 たったこれだけで組織図完成
  11. 11. 関連テーブル user.rb section.rb class Section < ActiveRecord::Baseclass CreateUsers < ActiveRecord::Migration acts_as_nested_set def self.up acts_as_section_map create_table :users do ¦t¦ has_many :users t.column :name, :string, :null=>false end t.column :section_id, :integer end end def self.down drop_table :users endendclass User < ActiveRecord::Base belongs_to :sectionend
  12. 12. データ作成 userを作成し、sectionにぶら下げる% ./script/runner Section.find_by_name("Akasaka.rb").users << User.create(:name=>"takai")% ./script/runner Section.find_by_name("Akasaka.rb").users << User.create(:name=>"koichiroo")% ./script/runner Section.find_by_name("Akasaka.rb").users << User.create(:name=>"takedasoft")% ./script/runner Section.find_by_name("Asakusa.rb").users << User.create(:name=>"a_matsuda")% ./script/runner Section.find_by_name("Asakusa.rb").users << User.create(:name=>"kakutani")% ./script/runner Section.find_by_name("Asakusa.rb").users << User.create(:name=>"maiha")
  13. 13. ビューも直すindex.rhtml<% section_map(@table2) do ¦table¦ %> <%=table.name%> <ul> <% table.users.each do ¦user¦%> <li><%=user.name%></li> <%end%> </ul><% end %>
  14. 14. 見た目を直そうindex.rhtml<% section_map(@table2,:table=>{:cellspacing=>"0",:style=>"border-collapse:collapse;"}, :td=>{:valign=>"top",:style=>"border:1px solid #666666;color:navy;"}) do ¦table¦ %> <%=table.name%> <ul> <% table.users.each do ¦user¦%> <li style="font-size:12px;color:green"><%=user.name%></li> <%end%> </ul><% end %>
  15. 15. のろくない?数が多いとのろいぞでも、なんとかしちゃるでも今日はおしまい続きは次回以降
  16. 16. イケテルDB チューニングxibbarこと藤岡岳之
  17. 17. ブログのぶくま
  18. 18. これでぎりぎり卒論になるんじゃなかろうか。長谷川がこけたら、これにしようかな。。。 なんだこれ!!
  19. 19. 巨大な組織(1..4).each{¦n¦Section.create(:name=>"Section:#{n}")}(5..60).each{¦n¦2.times{¦m¦Section.find_or_create_by_name("Section:#{n*2+m+3}").move_to_child_ofSection.find_by_name("Section:#{n}")}} 120組織のツリーを作るスクリプト
  20. 20. 遅すぎる User Load (0.000204) SELECT * FROM usersWHERE (users.section_id = 124)Completed in 18.13171 (0 reqs/sec) ¦ Rendering:7.49291 (41%) ¦ DB: 10.46457 (57%) ¦ 200 OK[http://localhost/welcome] 表示に18秒っておい!
  21. 21. インデックスを張ってみる User Load (0.000182) SELECT * FROM usersWHERE (users.section_id = 124)Completed in 10.01978 (0 reqs/sec) ¦ Rendering:6.97071 (69%) ¦ DB: 2.87357 (28%) ¦ 200 OK[http://localhost/welcome] 10秒に短縮! まだ使い物にならないけど
  22. 22. すばやさの種を投入class AddDepthToSections < ActiveRecord::Migration def self.up add_column :sections,:depth,:integer Section.set_depth end def self.down remove_column :sections,:depth endend depthという項目を追加し、 値をセットする。depthというのは 文字通り深さの値(levelの結果)
  23. 23. すばやさの種の結果 User Load (0.000180) SELECT * FROM users WHERE(users.section_id = 124)Completed in 0.84391 (1 reqs/sec) ¦ Rendering: 0.40329 (47%) ¦DB: 0.27316 (32%) ¦ 200 OK [http://localhost/welcome] 1秒切ってメデタシメデタシ
  24. 24. ちょっとだけ解説のろさの原因はsectionが自分自身の深さを知るためにselect count(*)していることにある。(levelメソッド)これをカラムとして持つとツリーの構築の早さが圧倒的に違う(つまりdepthカラム)
  25. 25. benchmark% ./script/performance/benchmarker 100 Section.find(:all).map(&:level) user system total real#1 5.790000 0.470000 6.260000 ( 10.331682)% ./script/performance/benchmarker 100 Section.find(:all).map(&:depth) user system total real#1 0.610000 0.010000 0.620000 ( 0.784886) ちなみにindex張る前は% ./script/performance/benchmarker 100 Section.find(:all).map(&:level) user system total real#1 6.180000 0.530000 6.710000 ( 20.693455) 20.693 0.784=26.394 26倍早くなった!!
  26. 26. グーおしまい http://www001.upp.so-net.ne.jp/masa_gallery/edo.html

×