6

Rails 4 について。 最近、開発環境に bullet gem をインストールして、アプリの N+1 クエリをクリアしました。関連モデル:

投稿: カテゴリとユーザーに属します。多くの SubmissionDetail があります。

ユーザー:多くの提出物があります

カテゴリ:多数の投稿があります。アワード所属。

アワード:多くのカテゴリがあります (およびカテゴリを介した提出)

SubmissionDetails: Submissionsに属します

投稿のインデックス ページにはeach do、現在のユーザーが行った各投稿を表示するステートメントがあります。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>

その後、関連するカテゴリ、賞、提出物の詳細情報など、提出物に関する情報を一覧表示します。Bullet は、このステートメントに N+1 の問題があると言っています。

N+1 Query detected Submission => [:category] Add to your finder: :include => [:category]
N+1 Query detected Submission => [:user] Add to your finder: :include => [:user]
N+1 Query detected Submission => [:submission_details] Add to your finder: :include => [:submission_details]

これら 3 つのモデルすべてを追加しようとするたびに.includes、最初にリストしたモデルのみが選択されます (これは驚くべきことではありません)。複数のモデルが関係している場合は、別のルートに進む必要があると思います-おそらく結合ステートメントですか?

(最初の項目を :category にすると、この通知が追加されます):

N+1 Query detected Category => [:award] Add to your finder: :include => [:award]

(そのため、ステートメントの一部として、Award をそこに適合させる方法も含める必要があります。これには、カテゴリを通じて多くの提出物があります)。

では、3 つの異なるモデルに対して1 つを実行できないと仮定すると.includes、別の方法はありますか? ありがとう。

4

2 に答える 2

9

より明確にするために、詳細をより見やすくしましょう。

class User < ActiveRecord::Base
  has_many :submissions
end

class Submission < ActiveRecord::Base
  belongs_to :category
  belongs_to :user
  has_many   :submission_details
end

class SubmissionDetail < ActiveRecord::Base
  belongs_to :submission
end

class Category < ActiveRecord::Base
  belongs_to :award
  has_many   :submissions
end

class Award < ActiveRecord::Base
  has_many :categories
  has_many :submissions, through: :categories
end

私の理解が正しければ、あなたの current_user について、あなたは彼の提出物をリストしています。提出物ごとに、submission_details とそれが属するカテゴリをリストします。すべてのカテゴリについて、賞もリストします。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>
      ...
      <div><%= submission.category %></div>
      <div><%= submission.category.award %></div>
      <%= submission.submissions_details.each do |submission_detail| %>
        ...
      <% end %>
    <% end %>
  </div>
<% end %> 

includes次の方法で使用することにより、N+1 問題を取り除くことができます。

current_user.submissions.includes(:submission_details, :category => :award)

詳細については、includes次を参照してください。

  1. Rails ガイドのeager-loading-association
  2. Rails ガイドのeager-loading-multiple-association
  3. Rails API - インクルード

協会

于 2014-05-20T22:05:06.403 に答える
2

これらの関連付けを含めるには、提出用のスコープを作成します。latestその間、スコープを追加します。

class Submission
  scope :eager,  -> { includes(:submission_details, :category => [:award]) }
  scope :latest, -> { order("created_at DESC") }
end

次に、単に

current_user.submissions.latest.eager [...]

を含める必要はありませんが:user、Rails はそのような参照についてあまり賢くないことに気付きました。

于 2014-05-20T22:10:28.583 に答える