User.allを使わない。selectへ。


本記事の目的: モデルから取り出す際に、不要なデータ量を抑える

結論

user_controller.rb(変更前
:
def index
  @users = User.all.page(params[:page])
end

def following
  @title = "フォロー"
  @user = User.find(params[:id])
  @users = @user.following.page(params[:page])
  render "show_follow"
end
:
user_controller.rb(変更後
:
def index
  @users = User.select(:id, :username).
        includes([:works, avatar_attachment: :blob]).
        page(params[:page])
end

def following
  @title = "フォロー"
  @user = User.find(params[:id])
  @users = @user.following.select(:id, :username).
        includes([:works, avatar_attachment: :blob]).page(params[:page])
  render "show_follow"
end
:

本文

例の如くN+1問題の解消に取り掛かるべく、includesを用いて関連付けされているデータを同時に取り出す事で、クエリの発行を抑えていた。

そこでもう一つ。User.allの利用をやめることにした。理由としては、モデルから取り出す不要なデータ量を抑えるためである。解決方法としては、selectメソッドを用いることで、必要なデータだけを指定することとする。

具体例を以下に示します。

User.all
=>   TRANSACTION (0.4ms)  BEGIN
  User Load (0.8ms)  SELECT `users`.* FROM `users`
[#<User id: 1, username: "Test_User", email: "[email protected]", created_at: "2021-05-03 13:07:22.877662000 +0900", updated_at: "2021-05-03 13:07:58.184592000 +0900", uid: nil, provider: nil, description: "My name is Test_User", website: "http://example.com/">,
 #<User id: 2, username: "Mon Mothma", email: "[email protected]", created_at: "2021-05-03 13:07:23.807304000 +0900", updated_at: "2021-05-03 13:07:58.296445000 +0900", uid: nil, provider: nil, description: "My name is Savage Opress", website: "http://example.com/">,
 :
 #<User id: 22, username: "Bail Organa", email: "[email protected]", created_at: "2021-05-03 13:07:29.297810000 +0900", updated_at: "2021-05-03 13:07:29.297810000 +0900", uid: nil, provider: nil, description: "My name is Nute Gunray", website: "http://example.com/">]
User.select(:username, :id)
=>   User Load (1.3ms)  SELECT `users`.`username`, `users`.`id` FROM `users`
[#<User username: "Test_User", id: 1>,
 #<User username: "Mon Mothma", id: 2>,
 :
 #<User username: "Bail Organa", id: 22>]

以上のように結果は歴然であるが、必要なデータだけを取り出すことに成功した。ちなみにこれは、2行目にあるActiveRecordが発行するSQLの違いにより発生している。

User.all
=>   User Load (0.8ms)  SELECT `users`.* FROM `users`

User.select(:username, :id)
=>   User Load (1.3ms)  SELECT `users`.`username`, `users`.`id` FROM `users`

また、これは関連付けを行っているモデルにおいても同様の事が可能であった。

これもActiveRecordが発行するクエリに準じている事がわかる。

  pry(main)> user = User.first
  User Load (1.9ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, username: "Test_User", email: "[email protected]", created_at: "2021-05-03 13:07:22.877662000 +0900", updated_at: "2021-05-03 13:07:58.184592000 +0900", uid: nil, provider: nil, description: "My name is Test_User", website: "http://example.com/">

# 変更前
  pry(main)> user.following
=>   User Load (0.9ms)  SELECT `users`.* FROM `users` INNER JOIN `relationships` ON `users`.`id` = `relationships`.`followed_id` WHERE `relationships`.`follower_id` = 1
[#<User id: 3, username: "Lyra Erso", email: "[email protected]", created_at: "2021-05-03 13:07:24.080580000 +0900", updated_at: "2021-05-03 13:07:58.435753000 +0900", uid: nil, provider: nil, description: "My name is Jango Fett", website: "http://example.com/">,
 #<User id: 4, username: "Borvo the Hutt", email: "[email protected]", created_at: "2021-05-03 13:07:24.351829000 +0900", updated_at: "2021-05-03 13:07:58.993427000 +0900", uid: nil, provider: nil, description: "My name is Darth Sidious", website: "http://example.com/">,
 :
 #<User id: 21, username: "Mon Mothma", email: "[email protected]", created_at: "2021-05-03 13:07:29.023478000 +0900", updated_at: "2021-05-03 13:07:29.023478000 +0900", uid: nil, provider: nil, description: "My name is Jango Fett", website: "http://example.com/">]

# 変更後
  pry(main)> user.following.select(:id, :username)
=>   User Load (1.7ms)  SELECT `users`.`id`, `users`.`username` FROM `users` INNER JOIN `relationships` ON `users`.`id` = `relationships`.`followed_id` WHERE `relationships`.`follower_id` = 1
[#<User id: 3, username: "Lyra Erso">,
 #<User id: 4, username: "Borvo the Hutt">,
 :
 #<User id: 21, username: "Mon Mothma">]

以上です!

最後まで読んで頂きありがとうございました!