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

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

詳細はこちら
SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

Q&A

解決済

1回答

3707閲覧

Rails N+1問題

amedama

総合スコア37

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

0グッド

2クリップ

投稿2019/09/15 23:09

編集2019/09/16 00:53

ブログアプリを製作中です。
機能の一部としてユーザーが記事をストックしたり、ライクしたりができます。

表示速度を改善するためにN+1問題を解決しようとしています。
いくつかのN+1問題は解決できたのですが、検索、検討しても解決できない問題が出てきました。

現在、ユーザーのステータスに応じて、ストック機能、ライク機能のアイコン表示を変えています。
その際、ストックされているか(liked?)、ライクされているか(stocked?)を検証する過程でN+1問題が発生しているようです。

Userモデル has_many :articles, dependent: :destroy has_many :likes, dependent: :destroy has_many :article_stocks, dependent: :destroy
Articleモデル belongs_to :user has_many :likes, dependent: :destroy has_many :liked_users, through: :likes, source: :user has_many :article_stocks, dependent: :destroy has_many :stocked_user, through: :article_stocks, source: :user def liked?(user) liked_users.include?(user) end def stocked?(user) stocked_user.include?(user) end
Likeモデル belongs_to :user belongs_to :article counter_culture :article
Article_stockモデル belongs_to :user belongs_to :article counter_culture :article
aritcles_controller @q = Article.with_attached_image.includes(:tags, user: { image_attachment: :blob }).includes(:liked_users, :stocked_user).ransack(params[:q]) @articles = @q.result(distinct: true).page(params[:page] **.includes(:liked_users, :stocked_user)←この部分を正したいです。**
article/index.html.erb <% @articles.each do |article| %> <span id="like-of-<%= article.id %>"> <%= render "likes/like", article: article %> </span> <span id="stock-of-<%= article.id %> "> <%= render "article_stocks/stock", article: article %> </span <% end %>
likes/like <% if current_user != article.user && user_signed_in? %> <span class="like"> **<% if article.liked?(current_user) %> ←この部分でN+1が発生しています。** <%= link_to "", like_path(article.likes.find_by(user_id: current_user.id)), method: :delete, class:"fas fa-heart", remote: true %> <%= link_to "#{article.likes_count}", likes_path(article_id: article.id)%> <% else %> <%= link_to "", likes_path(article_id: article.id), method: :post, class:"far fa-heart", remote: true %> <%= link_to "#{article.likes_count}", likes_path(article_id: article.id)%> <% end %> </span> <% else %> <span class="far fa-heart" style="color:gray;"></span> <%= link_to "#{article.likes_count}", likes_path(article_id: article.id), :style=> "color: gray;" %> <% end %>
article_stocks/stock <% if user_signed_in? %> <span class="stock"> **<% if article.stocked?(current_user) %> ←この部分でN+1がおきています。** <%= link_to article_stock_path(article.article_stocks.find_by(user_id: current_user.id)), method: :delete , remote: true, data: { confirm: 'ストックをはずしますか?' } do %> <%= content_tag :i, "", class:"fas fa-folder", :style=>"color: brown;" %> <%= article.article_stocks_count %> <% end %> <% else %> <%= link_to article_stocks_path(article_id: article.id), method: :post , remote: true , data: { confirm: 'ストックしますか?' }, :style=>"color: gray;" do %> <%= content_tag :i, "", class:"far fa-folder-open" %> <%= article.article_stocks_count %> <% end %> </span> <% end %> <% else %> <span style="color:gray;"> <%= content_tag :span, nil, class:"far fa-folder-open" %> <%= article.article_stocks_count %> </span> <% end %>

他に必要な情報がありましたら、ご指摘お願いします。

解決策わかる方ご教授よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

私もN+1では悩まされ続けている身ですので、正解には程遠いかもですが、、、
拝見した一見ではなぜこれでだめ? なのですが。

bulletはお使いですか?もし使っていなかったでしたら試してみて下さい。
余分なincludesや足りないものを指摘してくれます。時々??もありますが。

includes(:tags, user: { image_attachment: :blob })部分ですが、viewをみたところ tags、user が使われていないように見えます。
もしそうでしたらここを削除してみたらどうなるでしょう?

複雑なincludesを作った時の経験なのですが
includes(:tags, user: { image_attachment: :blob }, :liked_users, :stocked_user)
の様になっていると user: { } の後ろがうまく反映しないことがありました。
user: {} を一番後ろに移すとどうなりますか?

投稿2019/09/16 00:08

winterboum

総合スコア23567

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

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

amedama

2019/09/16 00:28

winterboumさん、こんにちは。 ありがとうございます。 bullet使ってます!(githubに挙げたコードは一時的にbulletをコメントアウトしていますが...) bulletで警告でてしまっている状態です。 すみません、コードが見やすくなるように関係のあるであろうところを抜粋して載せたのでtagsとuserはviewの中で出てきています。 q = Article.with_attached_image.includes(:liked_users, :stocked_user, :tags, user: { image_attachment: :blob }).ransack(params[:q])で確認してみると、bulletの警告で AVOID eager loading detected Article => [:likes, :article_stocks] Remove from your findler: :includes => [:likes, :article_stocks] Call stack と出てしまいます。。。
amedama

2019/09/16 00:36 編集

.includes(:tags, user: { image_attachment: :blob }).ransack(params[:q])の場合、 Completed 200 OK in 1202ms (Views: 742.2ms | ActiveRecord: 457.3ms) となり、 .includes(:liked_users, :stocked_user, :tags, user: { image_attachment: :blob }).ransack(params[:q])の場合、 Completed 200 OK in 740ms (Views: 549.8ms | ActiveRecord: 185.8ms)となっているので速度は改善されているようです。 相変わらずbulletの警告が出ていますが、一旦は無視するのもひとつでしょうか??
winterboum

2019/09/16 00:44

警告の中に、何を追加しろとかあるかと思いますが、それはつけてある、ということですよね? 開発がまだまだあるならとりあえず気にしない、かもですが データが大きくなると影響でてきますから、どこかで戻らないとならんかも logで「確かに読んでる、どこで読んでる」というのを突き止めて個別に対応するのもありかと。 これから「敬老の日の集い」の運営で出ますので、次にここに来るのは夕方になります。
amedama

2019/09/16 00:52

ありがとうございます。 速度改善されているのでさらなる機能改善としては一回保留して、こちらの質問は解決にします。 ご助言ありがとうございました。 お出かけお気をつけください!
winterboum

2019/09/16 08:16

N+1と直接関係は無いのですが liked_users.include?(user) ですとUserモデルを取り込みますね。Userって結構大きいのでは? 格好わるいけど likes.any?{|like| like.user_id == user.id} likes.map(&:user_id).include?(user.id) とかは? うえのほうが負荷軽いかな
amedama

2019/09/17 09:12

おお!!ありがとうございます。 そういう書き方もありますね! まだひよっこなので負荷のことに関してまではそこまで考えが至ってなかったので、非常にありがたいです。 winterboumさんのおかげで勉強になりました。 重ね重ねありがとうございます。 負荷を減らすRubyの書き方も意識して行きたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問