Rails
STI
テーブル

RailsのSTIを使ってみた

背景

既に運用中の求人サイトへ違う種類の求人を追加する要望が来た
現状の求人テーブルはjob_offersというテーブルがあり、中に既に数十万件データあり
job_offersとリレーションしてるテーブルが10個ぐらいがある

どう対応するか考える

別テーブルで追加するか既存のテーブルへ追加するか検討してみた結果
業務フローと修正コストなどの観点で既存のテーブルへ追加するすることにした

job_offersテーブル前後

STI(単一テーブル継承)を利用してテーブル設計

before

id content
1 既存求人

after

typeカラムを追加して、1の場合は既存求人
2,3は新規タイプの求人

id type content
1 1 既存求人タイプA
1 2 新規求人タイプB

対応詳細

カラム追加してmigrate

class AddTypeToJobOffers < ActiveRecord::Migration
  def change
    add_column :job_offers, :type, :integer, limit: 2, default: 1, null: false, after: :id
    add_index  :job_offers, :type
  end
end

新規タイプ求人クラス定義

class JobOffer
end

class TypeAJobOffer < JobOffer
end

class TypeBJobOffer < JobOffer
end

stiカラムをintegerにする

Railsのデフォルトではtypeをstringとして利用されて、中身クラス名を入れるようになってる
既存テーブルの件数が多くて、string利用する場合は遅いため、integerで対応するようにした

module JobOfferStiable
  extend ActiveSupport::Concern

  module ClassMethods
    def find_sti_class(type_name)
      case type_name
        when 1
          TypeAJobOffer
        when 2
          TypeBJobOffer
        else
          self
      end
    end

    def sti_name
      case self.to_s
        when 'TypeAJobOffer'
          1
        when 'TypeBJobOffer'
          2
        else
          0
      end
    end
  end
end
class JobOffer
  include JobOfferStiable
end

関連付け追加

顧客は複数の求人を持っている、TypeAとTypeBの求人関連付けを追加する

class Customer
    has_many :job_offers
    has_many :type_a_job_offers
    has_many :type_b_job_offers
end

テスト

# 全ての求人
JobOffer.count
sql: SELECT COUNT(*) FROM `job_offers`;

# TypeAの求人
TypeAJobOffer.count
sql: SELECT COUNT(*) FROM `job_offers` WHERE `job_offers`.`type` IN (1);

# TypeB求人
TypeBJobOffer.count
sql: SELECT COUNT(*) FROM `job_offers` WHERE `job_offers`.`type` IN (2);

# 顧客の全ての求人
Customer.last.job_offers

# 顧客の全てのTypeA求人
Customer.last.type_a_job_offers

# 顧客の全てのTypeB求人
Customer.last.type_b_job_offers

その他

開発中にSTIを無効にしたい

開発中に、他のブランチに切り替えして、STIテーブルを自動判定されるため、エラーになる
その場合は、STIを無効にすれば良い

def JobOffer
  self.inheritance_column = :_type_disabled
end

参考文書

http://ruby-rails.hatenadiary.com/entry/20141206/1417839458
https://github.com/viola/sti-example
https://qiita.com/taka0125/items/5a968817ff61aeb67107