例として次のようなクラスがあると想定します。
class User < ApplicationRecord
has_many :comments
end
class Article < ApplicationRecord
belongs_to :user
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :article
end
item_type
が "Article"
の Comment
を持つユーザーを取得したい場合、 joins
と where
を使うと以下のようになります。
User.joins(:comments).where(comments: { item_type: "Article" })
これを merge
を使って書き換えると次のようになります。
User.joins(:comments).merge(Comment.where(item_type: "Article"))
試しに、出力されるSQLを確認してみると、同じSQLが実行されることがわかります。
[17] (nobody)@development pry(main)> ::User.joins(:comments).where(comments: { item_type: "Article" }).to_sql
=> "SELECT `users`.* FROM `users` INNER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`item_type` = 'Article'"
[18] (nobody)@development pry(main)> ::User.joins(:comments).merge(::Comment.where(item_type: "Article")).to_sql
=> "SELECT `users`.* FROM `users` INNER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`item_type` = 'Article'"
上記の例ではあまりメリットが感じられませんが、以下のように複数 join する場合、 merge
を使った書き方の方がわかりやすく短いコードで済むことが多いので、このテクニックを覚えておくことをオススメします!
# merge を使わない場合
User.joins(comments: { article: :tags }).where(comments: { article: { tags: { name: "Haskell" }}})
# merge を使った場合
User.joins(comments: { article: :tags }).merge(Tag.where(name: "Haskell"))