背景
既に運用中の求人サイトへ違う種類の求人を追加する要望が来た
現状の求人テーブルは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