RailsのN+1避けのものもう一つ作った
RailsでFoo.where(condition).n1_safeと付け加えるだけで簡単にN+1クエリを回避出来る奴
https://github.com/tompng/n1_safe
使い方
gem 'n1_safe', github: 'tompng/n1_safe'
@posts = Post.all.includes(:comments,ほげほげ)
みたくincludesする代わりに
@posts = Post.all.n1_safe
@post = Post.find(params[:id]).n1_safe
とつけ加えるだけでN+1が起こらなくなる。
@posts.each{|post| post.comments.each{|comment| comment.user.id comment.favs.count } }
SQLはこんな感じ
Comment Load (0.8ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" IN (1, 2, ... 19, 20)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (20, 16, ... 15, 4)
(0.5ms) SELECT COUNT(*) AS count_all, target_id AS target_id FROM "favs" WHERE "favs"."target_id" IN (49, 73, ... 55, 67) AND "favs"."target_type" = ? GROUP BY "favs"."target_id" [["target_type", "Comment"]]
↑可能ならgroup_by/countもやるよ 出来ない場合(has_many throughの場合など)はpreloadしてto_a.sizeで代用
前作ったsmart_jsonは依存関係を定義から読んで先にincludesする方針。
今回は呼ばれて必要になった時にあとからpreloadする方針。
n1_safeを呼んだルートとなるオブジェクトを覚えておいて、
関連モデルを呼び出した時とRelationを配列にする時に、それを起点にpreloadする。
・BasicObject便利
・ActiveRecord::Associations::Preloader便利
・バージョン違い辛い
ActiveRecord::Base#reflectionsが無くなってたりreflectionsのkeyがsymbolかstringか違ったり