Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Railsスコープをプリロードする方法

この記事は、Soonsang Hongのおかげで、韓国語でも利用できます!

Railsのスコープを使用すると、必要なレコードを簡単に見つけることができます。

app / models / review.rb
class Review < ActiveRecord::Base
  belongs_to :restaurant

  scope :positive, -> { where("rating > 3.0") }
end
irb(main):001:0> Restaurant.first.reviews.positive.count
  Restaurant Load (0.4ms)  SELECT  `restaurants`.* FROM `restaurants`  ORDER BY `restaurants`.`id` ASC LIMIT 1
   (0.6ms)  SELECT COUNT(*) FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0)
=> 5

ただし、注意しないと、アプリのパフォーマンスが大幅に低下します。

なんで? スコープを実際にプリロードすることはできません。 したがって、肯定的なレビューでいくつかのレストランを表示しようとした場合:

irb(main):001:0> restauraunts = Restaurant.first(5)
irb(main):002:0> restauraunts.map do |restaurant|
irb(main):003:1*   "#{restaurant.name}: #{restaurant.reviews.positive.length} positive reviews."
irb(main):004:1> end
  Review Load (0.6ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0)
  Review Load (0.5ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 2 AND (rating > 3.0)
  Review Load (0.7ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 3 AND (rating > 3.0)
  Review Load (0.7ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 4 AND (rating > 3.0)
  Review Load (0.7ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 5 AND (rating > 3.0)
=> ["Judd's Pub: 5 positive reviews.", "Felix's Nightclub: 6 positive reviews.", "Mabel's Burrito Shack: 7 positive reviews.", "Kendall's Burrito Shack: 2 positive reviews.", "Elisabeth's Deli: 15 positive reviews."]

はい、それはN+1クエリです。 Railsアプリが遅い最大の原因。

ただし、関係について別の方法で考えると、これは非常に簡単に修正できます。

スコープを関連付けに変換

belongs_toなどのRails関連付けメソッドを使用する場合 およびhas_many 、モデルは通常次のようになります:

app / models / restaurant.rb
class Restaurant < ActiveRecord::Base
  has_many :reviews
end

ただし、ドキュメントを確認すると、さらに多くのことができることがわかります。これらのメソッドに他のパラメーターを渡して、それらの動作を変更することができます。

スコープ 最も便利なものの1つです。 スコープと同じように機能します 以前から:

app / models / restaurant.rb
class Restaurant < ActiveRecord::Base
  has_many :reviews
  has_many :positive_reviews, -> { where("rating > 3.0") }, class_name: "Review"
end
irb(main):001:0> Restaurant.first.positive_reviews.count
  Restaurant Load (0.2ms)  SELECT  `restaurants`.* FROM `restaurants`  ORDER BY `restaurants`.`id` ASC LIMIT 1
   (0.4ms)  SELECT COUNT(*) FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0)
=> 5

これで、 includeを使用して新しい関連付けをプリロードできます :

irb(main):001:0> restauraunts = Restaurant.includes(:positive_reviews).first(5)
  Restaurant Load (0.3ms)  SELECT  `restaurants`.* FROM `restaurants`  ORDER BY `restaurants`.`id` ASC LIMIT 5
  Review Load (1.2ms)  SELECT `reviews`.* FROM `reviews` WHERE (rating > 3.0) AND `reviews`.`restaurant_id` IN (1, 2, 3, 4, 5)
irb(main):002:0> restauraunts.map do |restaurant|
irb(main):003:1*   "#{restaurant.name}: #{restaurant.positive_reviews.length} positive reviews."
irb(main):004:1> end
=> ["Judd's Pub: 5 positive reviews.", "Felix's Nightclub: 6 positive reviews.", "Mabel's Burrito Shack: 7 positive reviews.", "Kendall's Burrito Shack: 2 positive reviews.", "Elisabeth's Deli: 15 positive reviews."]

6回のSQL呼び出しではなく、2回だけ実行しました。

class_nameを使用 、同じオブジェクトに複数の関連付けを行うことができます。これはかなり頻繁に役立ちます。)

複製はどうですか?

ここにはまだ問題があるかもしれません。 where( "rating> 3.0") これでレストランクラスに参加しました。後で肯定的なレビューをrating>3.5に変更した場合 、2回更新する必要があります!

さらに悪いことに、ある人がこれまでに残した肯定的なレビューもすべて取得したい場合は、そのスコープをUserクラスにも複製する必要があります:

app / models / user.rb
class User < ActiveRecord::Base
  has_many :reviews
  has_many :positive_reviews, -> { where("rating > 3.0") }, class_name: "Review"
end

あまり乾燥していません。

ただし、これを回避する簡単な方法があります。 whereの内部 、positiveを使用できます Reviewクラスに追加したスコープ:

app / models / restaurant.rb
class Restaurant < ActiveRecord::Base
  has_many :reviews
  has_many :positive_reviews, -> { positive }, class_name: "Review"
end

そうすれば、アイデア レビューを肯定的なレビューにする理由は、まだ1か所だけです。

スコープは素晴らしいです。適切な場所で、データのクエリを簡単かつ楽しくすることができます。ただし、N + 1クエリを避けたい場合は、注意する必要があります。

したがって、スコープが問題を引き起こし始めた場合は、スコープを関連付けにラップしてプリロードします 。それほど手間がかからず、SQL呼び出しを大幅に節約できます。


  1. Rubyを使用してコマンドラインアプリケーション(CLI)を構築する方法

    多くの人は、RubyがWebアプリケーションではないことを実行できることを忘れています。この記事では、それを改善するのに役立つコマンドラインアプリケーションを構築する方法を紹介したいと思います! 使い慣れているコマンドラインアプリケーションは次のとおりです。 psql rails bundler gem git コマンドラインアプリケーションを構築する方法はたくさんあります。この記事では、そのうちの3つに焦点を当てます。 あなたは学ぶつもりです : ARGVアレイ OptParseライブラリ トールの宝石 始めましょう! RubyARGV定数 コマンドラインア

  2. RailsアプリケーションでOmniAuth-Twitterを使用する方法

    このチュートリアルでは、アプリケーションのユーザーがTwitterアカウントを使用してログインできるようにする方法を学習します。これを行うには、OAuthなどのツールを使用すると簡単になります。 OmniAuthのTwitter戦略を含むOmniAuth-Twitterを利用します。 飛び込みましょう! はじめに Railsアプリケーションを生成することから始めます。ターミナルから、コマンドを実行して実行します。 rails new Tuts-Social -T Gemfileを開き、ブートストラップgemを追加します。 #Gemfile...gem bootstra