ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法

ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法メモ。



ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法


ActiveRecord4でhas_manyリレーションを定義する際、逆方向のリレーションをinverse_ofで指定することで、オブジェクトをメモリ空間内で効率化することができます。


例えば下記のようなモデルがあるとします。


1class Dungeon < ActiveRecord::Base
2  has_many :traps
3end
4 
5class Trap < ActiveRecord::Base
6  belongs_to :dungeon
7end

上記のリレーションにおいて、DungeonのtrapsとTrapのDungeonは逆方向のリレーションであるため、同一のオブジェクトはメモリ内でも同一に扱うことができるはずです。


しかし、ActiveRecord4のデフォルトの動作ではそのような最適化は行ってくれません。


1d = Dungeon.first
2t = d.traps.first
3d.level == t.dungeon.level # => true
4d.level = 10
5d.level == t.dungeon.level # => false

上記のコードにおいて、dとt.dungeonはDB内の同一のオブジェクトを指しています。しかし、d.level == t.dungeon.levelはfalseを返します。


これは、dとt.dungeonがメモリ内では別のオブジェクトとして扱われていることが原因です。inverse_ofを明示的に指定することで、この非効率な状態を改善することができます。


1class Dungeon < ActiveRecord::Base
2  has_many :traps, inverse_of: :dungeon
3end
4 
5class Trap < ActiveRecord::Base
6  belongs_to :dungeon, inverse_of: :traps
7end

上記のように明示的にinverse_ofを指定することで、前述のdとt.dungeonはメモリ内で同一のインスタンスとして扱われるようになり、d.level == t.dungeon.levelがtrueを返すようになります。


ただし、inverse_ofにはいくつか制限がある点に注意が必要です。


  1. throughアソシエーションと一緒には動きません
  2. polymorphicアソシエーションと一緒には動きません
  3. for belongs_to associations has_many inverse associations are ignored.


参考リンク


ActiveRecord::Associations::ClassMethods


著者プロフィール
Webサイトをいくつか作っています。
著者プロフィール