Rails

【Rails】階層構造を簡単に実装できる「gem ‘ancestry’」について簡単にまとめてみた

Rails

 

今回は、階層構造を簡単に実装することができる「gem ‘ancestry’」について簡単にまとめたので解説したいと思います。カテゴリー実装の際によく使われるgemです。

ancestryを使えば、親カテゴリー・子カテゴリー・孫カテゴリーっていう感じで実装できそうだね。

ancestryとは

ancestryとは、簡単に階層構造(ツリー構造)を実装することができるgemです。直訳すると「祖先」という意味になり、親子孫関係を作成することができると想像できます。

ancestryを用いたカテゴリー実装の例を見ていきましょう。

ancestry簡略図

 

上記の図を見て分かる通り、ancestryカラムの親カテゴリーはnull、子カテゴリーには親カテゴリーのidが、孫カテゴリーには親カテゴリーと子カテゴリーのidが1/2というような形で入ります。

↓ SequelProで確認した結果

Image from Gyazo

ancestryを使用するメリット

ancestryを使用するメリットとしては、独自のメソッドを使えるということが挙げられます。使用可能なメソッドは公式ドキュメントに全て掲載されているので、あらかじめ目を通しておきましょう。

↓ ancestryのメソッド(一部抜粋)

Image from Gyazo

 

実際にメソッドを使ってみます。一例であるため、モデル名とカラム名はそれぞれ適当に置き換えていただければと思います。

まずはターミナルで「rails c」を実行し、コンソール画面を開きます。その後、rootとchildとgrandchildにそれぞれ値を入れていきます。

pry > root  = Maincategory.create(name: '親だよ')
   (2.2ms)  BEGIN
  Maincategory Create (14.3ms)  INSERT INTO `maincategories` (`name`, `created_at`, `updated_at`) VALUES ('親だよ', '2020-04-20 06:39:05', '2020-04-20 06:39:05')
   (4.5ms)  COMMIT
=> #<Maincategory id: 1322, name: "親だよ", ancestry: nil, created_at: "2020-04-20 06:39:05", updated_at: "2020-04-20 06:39:05">

pry > child = root.children.create(name: '子供だよ')
   (0.3ms)  BEGIN
  Maincategory Create (0.4ms)  INSERT INTO `maincategories` (`name`, `ancestry`, `created_at`, `updated_at`) VALUES ('子供だよ', '1326', '2020-04-20 06:48:06', '2020-04-20 06:48:06')
   (0.8ms)  COMMIT
=> #<Maincategory id: 1327, name: "子供だよ", ancestry: "1326", created_at: "2020-04-20 06:48:06", updated_at: "2020-04-20 06:48:06">

pry > grandchild = child.children.create(name: '孫だよ')
irb(main):003:0> grandchild = child.children.create(name: '孫だよ')
   (0.3ms)  BEGIN
  Maincategory Create (0.8ms)  INSERT INTO `maincategories` (`name`, `ancestry`, `created_at`, `updated_at`) VALUES ('孫だよ', '1326/1327', '2020-04-20 06:48:22', '2020-04-20 06:48:22')
   (0.9ms)  COMMIT
=> #<Maincategory id: 1328, name: "孫だよ", ancestry: "1326/1327", created_at: "2020-04-20 06:48:22", updated_at: "2020-04-20 06:48:22">

 

ここで「root.descendants」を実行すると子孫ノードが、「grandchild.ancestors」を実行すると祖先ノードが返ってきます。

pry > root.descendants
  Maincategory Load (0.8ms)  SELECT  `maincategories`.* FROM `maincategories` WHERE (`maincategories`.`ancestry` LIKE '1326/%' OR `maincategories`.`ancestry` = '1326') LIMIT 11
=> #<ActiveRecord::Relation [#<Maincategory id: 1327, name: "子供だよ", ancestry: "1326", created_at: "2020-04-20 06:48:06", updated_at: "2020-04-20 06:48:06">, #<Maincategory id: 1328, name: "孫だよ", ancestry: "1326/1327", created_at: "2020-04-20 06:48:22", updated_at: "2020-04-20 06:48:22">]>

pry > grandchild.ancestors
  Maincategory Load (4.3ms)  SELECT  `maincategories`.* FROM `maincategories` WHERE `maincategories`.`id` IN (1326, 1327) ORDER BY COALESCE(`maincategories`.`ancestry`, '') ASC LIMIT 11
=> #<ActiveRecord::Relation [#<Maincategory id: 1326, name: "親だよ", ancestry: nil, created_at: "2020-04-20 06:47:52", updated_at: "2020-04-20 06:47:52">, #<Maincategory id: 1327, name: "子供だよ", ancestry: "1326", created_at: "2020-04-20 06:48:06", updated_at: "2020-04-20 06:48:06">]>
こんな感じで、ancestryのメソッドを使うとDBとのやりとりが簡単になるね!

 

ちなみに言うと、ancestryには以下のようなデメリットも存在します。これらのデメリットを頭に入れた上で、実装を進めていかなればなりません。

  • ancestryカラムには1/14のように記述されているため、節点の親を変えるだけなのに節点の子のレコードも更新されてしまう
  • ancestry経由では問題ないが、DBの値を直接書き換えた際に整合性が取りにくい

ancestryの導入方法

ancestryの導入には、以下の3つのステップを抑えておけば大丈夫です。

  1. gem ‘ancestry’の記述
  2. テーブルにancestryカラムを追加
  3. 該当のモデルにhas_ancestryを記述

 

具体的に記述すると以下のようになります。

↓ Gemfile

gem 'ancestry'

 

↓ ターミナル

$ bundle install

 

↓ マイグレーションファイル(例)

class CreateMaincategories < ActiveRecord::Migration[5.2]
  def change
    create_table :maincategories do |t|
      t.string :name
      t.string :ancestry, index: true
      t.timestamps
    end
  end
end

 

↓ モデル(例)

class Maincategory < ApplicationRecord
  has_ancestry
end

まとめ

  • ancestryとは、簡単に階層構造(ツリー構造)を実装することができるgemである
  • 親カテゴリーはnull、子カテゴリーは1、孫カテゴリーは1/14のような形でancestryカラムに入る
  • ancestryのメリットは独自のメソッドを使えること
  • ancestryの導入の大まかな流れとしては、「gem ‘ancestry’の追加」→「テーブルにancestryカラムを追加」→「該当のモデルにhas_ancestryを追記」となる

参考

ancestry公式ドキュメント:https://github.com/stefankroes/ancestry

 

 

今回は階層構造を簡単に実装することができる「gem ‘ancestry’」について解説しました。カテゴリー実装の際によく使われるgemなので、ぜひこの機会に理解を深めておきましょう。