👬
【Rails】ActiveRecord の merge メソッドについての解説
概要
ActiveRecord の merge
メソッドは WHERE 句を追加、結合するために使われます。
複数の ActiveRecord リレーションを組み合わせることで、複雑な条件でも可読性を損なわずに書くことができます。
以下で使い方のパターンを紹介します。
merge メソッドの基本的な挙動
例えば以下のように、 ActiveRecord のクエリを 結合することができます。
# 元のコード
Article.where(title: "タイトル", body: "本文")
# merge を使ってクエリを結合するコード
title_query = Article.where(title: "タイトル")
body_query = Article.where(body: "本文")
title_query.merge(body_query)
# 1行で書くとこう
Article.where(title: "タイトル").merge(Article.where(body: "本文"))
このコードで発行される SQL は以下です。
SELECT
`articles`.*
FROM
`articles`
WHERE
`articles`.`title` = 'タイトル' AND `articles`.`body` = '本文'
ちなみに上記のクエリは作成されますが実行はされません。
そのあたりの遅延評価についてはこちらで解説してますので、合わせてどうぞ。
リレーションの結合
以下のようなモデルがあるとします。
class User < ApplicationRecord
has_many :articles
end
class Article < ApplicationRecord
belongs_to :user
end
「Hello World!」という title を持ったユーザーを検索したい場合、以下のように書けます。
title_query = Article.where(title: "Hello World!")
User.joins(:articles).merge(title_query)
# 1行で書くとこう
User.joins(:articles).merge(Article.where(title: "Hello World!"))
SELECT
`users`.*
FROM
`users`
INNER JOIN
`articles`
ON
`articles`.`user_id` = `users`.`id`
WHERE
`articles`.`title` = 'Hello World!'
スコープを使ったリレーションの結合
Article に is_publish: true
のレコードだけを取得できる scope が存在するとします。
class User < ApplicationRecord
has_many :articles
end
class Article < ApplicationRecord
belongs_to :user
# scope を追加
scope :published, -> { where(is_published: true) }
end
そのユーザーを取得したいとき、以下のように書けます。
active_articles = Article.published
User.joins(:articles).merge(active_articles)
# 1行で書くとこう
User.joins(:articles).merge(Article.published)
SELECT
`users`.*
FROM
`users`
INNER JOIN
`articles`
ON
`articles`.`user_id` = `users`.`id`
WHERE
`articles`.`is_published` = TRUE
複数条件の組み合わせ
以下の複数条件を組み合わせるときも merge を使えます。
- 20歳より上のユーザー
- "Helllo World!"というタイトルの記事を持っている
- その記事は公開されている
title_articles = Article.where(title: "Hello World!")
active_articles = Article.published
over_20_users = User.where("age > ?", 20)
User.joins(:articles)
.merge(over_20_users)
.merge(title_articles)
.merge(active_articles)
SELECT
`users`.*
FROM
`users`
INNER JOIN
`articles`
ON
`articles`.`user_id` = `users`.`id`
WHERE
(age > '20')
AND
`articles`.`title` = 'Hello World!'
AND
`articles`.`is_published` = TRUE
まとめ
merge
を使って複雑な条件でも可読性を保ちつつ、コードを書いていきましょう!
Discussion