🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

include

あるファイルで定義された関数や処理を、別のファイル上でも使用できるようにするプロセスをincludeと呼びます。

RubyGems

RubyGemsはRubyによるプログラミングのためのパッケージマネジメントツールです。ユーザはこれを使用することで、Rubyライブラリのダウンロードやアップデートや、依存関係の自動解決が可能になります。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

3033閲覧

トークルームがメッセージを持っているかどうかにより発生するN+1問題。

punchan36

総合スコア105

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

include

あるファイルで定義された関数や処理を、別のファイル上でも使用できるようにするプロセスをincludeと呼びます。

RubyGems

RubyGemsはRubyによるプログラミングのためのパッケージマネジメントツールです。ユーザはこれを使用することで、Rubyライブラリのダウンロードやアップデートや、依存関係の自動解決が可能になります。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2021/03/01 05:38

前提・実現したいこと

LINEの様なメッセージ機能を実装しました。
モデルは下記にて記載致しますが、UserMessageモデルがEntryRoomと言う中間テーブルを持っています。
ユーザーが他のユーザーとのトークルームを作成した時点で空のEntryが作成されますが、Messageを持たないEntryはトークルーム一覧画面(rooms/index.html.erb)には表示しないようにしています。

メッセージのやり取りや表示関連は既に実装出来ているのですが、N+1問題に関して一つ問題点があります。(尚、N+1問題解決の為にBulletと言うgemを使用し、問題が感知された場合はポップアップが表示されるようになっています。)

MessageのないEntryを持つユーザーがrooms/index.html.erbを表示すると、以下のポップアップが表示されます。

Ruby

1AVOID eager loading detected 2 Entry => [:user] 3 Remove from your query: .includes([:user]) 4Call stack

こちらはrooms_controller.rbにおいて、N+1問題を避けるためにEntry.includes(:room, :user)としている箇所があるのですが、この:user部分を省けと言われているようです。

しかし、通常Messageを持ったEntryで構成されたトークルーム一覧画面(rooms/index.html.erb)を表示する際、N+1問題を避けるために.includes(:user)は必要になります。

ですのでこれを省くと、Messageを持たないEntryを抱えているユーザーのページ上では問題が解決しますが、Messageを持つEntryのみを抱えているユーザーのページ上では、当然以下の様に逆に怒られます。

Ruby

1Use eager loading detected 2 Entry => [:user] 3 Add from your query: .includes([:user]) # :user 部分を付け加えて下さい。 4Call stack

該当のソースコード

user.rb

Ruby

1class User < ApplicationRecord 2 has_many :messages, dependent: :destroy 3 has_many :entries, dependent: :destroy 4 has_many :rooms, through: :entries 5end

entry.rb

class Entry < ApplicationRecord belongs_to :user belongs_to :room has_many :messages, through: :room end

room.rb

class Room < ApplicationRecord has_many :messages, dependent: :destroy has_many :entries, dependent: :destroy has_many :users, through: :entries end

message.rb

class Message < ApplicationRecord validates :content, {presence: true} belongs_to :user belongs_to :room has_one :entry, through: :room end

rooms_controller.rb

private def set_another_entries # ログイン中のユーザーの持つEntryを配列にする。 my_room_ids = @current_user.entries.pluck(:room_id) # 上で定義した「ログイン中のユーザーの持つEntry」の内、自分以外の相手一覧を表示する。 @another_entries = Entry.includes(:room, :user).where(room_id: my_room_ids).where('entries.user_id != ?', @current_user.id) @entries = @another_entries.includes(:messages).to_a # エントリーを配列にする。 .reject {|entry| entry.messages.blank?} # messageデータのないエントリーは表示において除外する。 .sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at} # messageが作成された順で並べ替える。 .reverse # Entryを降順に表示する。 end

試したこと

そもそも、Messageがあるかないかでなぜ:userの部分が引っ掛かるのかがよく分かりませんでした。
「Nilでない場合のみ実行する」と言う意味合いで以下の様に&.を用いて記述してみたりもしましたが、ダメでした…。

rooms_controller.rb

@another_entries = Entry&.includes(:room, :user).where(room_id: my_room_ids).where('entries.user_id != ?', @current_user.id)

どなたかご助言を頂けますと有難いです。

補足情報(FW/ツールのバージョンなど)

ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こちらはrooms_controller.rbにおいて、N+1問題を避けるためにEntry.includes(:room, :user)としている箇所があるのですが、この:user部分を省けと言われているようです。

「Eager Loadした先を全く使わない」という条件が実用上ほぼ発生しないのであれば、無視して構いません。下手に条件分岐を入れてメンテナンス性が下がるほうが厄介な問題となる危険が大きいです。

投稿2021/03/01 05:41

maisumakun

総合スコア145977

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/03/01 05:42

AVOID eager loadingを気にすべきなのは、ロードしたものの全く、あるいはほぼ使わないときだけで大丈夫です。
punchan36

2021/03/01 06:17

有難うございます! 「Eager Loadした先」とは、私の場合で言いますと各トークルームのユーザーやメッセージ情報で、これは繰り返し処理の回数分きちんと参照されるので記述はそのままで良い、と言う事でしょうか。 「Eager Loadした先を全く使わない」と言う状況がイメージ出来ませんでした…。 全く関係のないテーブル情報を間違ってincludeしてしまっている場合などでしょうか?
maisumakun

2021/03/01 06:41

> 全く関係のないテーブル情報を間違ってincludeしてしまっている場合などでしょうか? はい、そういう場合です(後からビューの側を改変したなどで、使わないものが残ってしまうことがあります)。
punchan36

2021/03/01 06:57

有難うございました。 ポップアップが表示された事で条件反射で編集に走ってしまいました。 大抵の場合、マストで考えなければならないのはUSE eager loadingの方ですね。勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問