本記事の目的: モデルから取り出す際に、不要なデータ量を抑える
結論
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: "test@example.com", 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: "test1@example.com", 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: "test21@example.com", 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: "test@example.com", 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: "test2@example.com", 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: "test3@example.com", 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: "test20@example.com", 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">]
以上です!
最後まで読んで頂きありがとうございました!