ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法メモ。
ActiveRecord4(Rails4)のリレーションをinverse_ofで最適化する方法
ActiveRecord4でhas_manyリレーションを定義する際、逆方向のリレーションをinverse_ofで指定することで、オブジェクトをメモリ空間内で効率化することができます。
例えば下記のようなモデルがあるとします。
1 | class Dungeon < ActiveRecord::Base |
5 | class Trap < ActiveRecord::Base |
上記のリレーションにおいて、DungeonのtrapsとTrapのDungeonは逆方向のリレーションであるため、同一のオブジェクトはメモリ内でも同一に扱うことができるはずです。
しかし、ActiveRecord4のデフォルトの動作ではそのような最適化は行ってくれません。
3 | d.level == t.dungeon.level |
5 | d.level == t.dungeon.level |
上記のコードにおいて、dとt.dungeonはDB内の同一のオブジェクトを指しています。しかし、d.level == t.dungeon.levelはfalseを返します。
これは、dとt.dungeonがメモリ内では別のオブジェクトとして扱われていることが原因です。inverse_ofを明示的に指定することで、この非効率な状態を改善することができます。
1 | class Dungeon < ActiveRecord::Base |
2 | has_many :traps, inverse_of: :dungeon |
5 | class Trap < ActiveRecord::Base |
6 | belongs_to :dungeon, inverse_of: :traps |
上記のように明示的にinverse_ofを指定することで、前述のdとt.dungeonはメモリ内で同一のインスタンスとして扱われるようになり、d.level == t.dungeon.levelがtrueを返すようになります。
ただし、inverse_ofにはいくつか制限がある点に注意が必要です。
- throughアソシエーションと一緒には動きません
- polymorphicアソシエーションと一緒には動きません
- for belongs_to associations has_many inverse associations are ignored.
参考リンク
ActiveRecord::Associations::ClassMethods