乱雑なコントローラーがより速くなるのに、なぜレールスコープが好ましいのですか?
-
29-09-2019 - |
質問
コントローラーで書いた長いロジックを使用するだけでなく、スコープを使用してアレルクエリをチェーンしようとしています。しかし、スコープは、すべてのレコードを取得してから、いくつかのロジックでそれらをふるいにかけるよりも遅くなります。なぜ、なぜスコープが良いのか疑問に思っています。
これが私がしていることです:
- 質問には多くの答えがあります
- 答えは1つの質問に属します
- 質問には、ソートに使用する「question_type」列があります
まず、スコープの方法...
Question.rb:
scope :answered, joins(:answers).order('answers.created_at desc')
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")
質問_controller.rb:
@dogs_recently_answered = Question.answered.dogs.uniq[0..9]
@cats_recently_answered = Question.answered.cats.uniq[0..9]
@mermaids_recently_answered = Question.answered.mermaids.uniq[0..9]
次に、ビューでは、これらのインスタンス変数(最大10個の要素を含む配列になりました)をサイクリングし、結果を表示します。
ページをロードするのにかかる時間は次のとおりです(5つの異なる時間)。
535msで200 okを完了しました(ビュー:189.6ms | Activerecord:46.2ms)
573msで200 okを完了しました(ビュー:186.0ms | Activerecord:46.3ms)
577msで200 OKを完了しました(ビュー:189.0ms | ActivereCord:45.6ms)
532msで200 okを完了しました(ビュー:182.9ms | Activerecord:46.1ms)
577msで200 okを完了しました(ビュー:186.7ms | Activerecord:46.9ms)
さて、乱雑なコントローラーの方法...
@answers = Answer.order("created_at desc")
@all_answered = []
@answers.each {|answer| @all_answered << answer.question}
@recently_answered = @all_answered.uniq
@dogs_all_answered = []
@cats_all_answered = []
@mermaids_all_answered = []
@recently_answered.each do |q|
if q.question_type == "dogs"
@dogs_all_answered << q
@dogs_recently_answered = @dogs_all_answered[0..9]
elsif q.question_type == "cats"
@cats_all_answered << q
@cats_recently_answered = @cats_all_answered[0..9]
elsif q.question_type == "mermaids"
@mermaids_all_answered << q
@mermaids_recently_answered = @mermaids_all_answered[0..9]
end
end
そして、これが今すぐページを読み込むのにかかる時間です(5つの異なる時間):
475msで200 OKを完了しました(ビュー:196.5ms | ActivereCord:34.5ms)
480msで200 okを完了しました(ビュー:200.4ms | Activerecord:36.4ms)
434msで200 okを完了しました(ビュー:198.2ms | Activerecord:35.8ms)
475msで200 okを完了しました(ビュー:194.2ms | Activerecord:36.4ms)
475msで200 OKを完了しました(ビュー:195.0ms | ActivereCord:35.4ms)
それで...
読みやすさは別として、スコープでクエリを磨くことで何が勝ちますか?より多くのレコードがあると、最終的には速くなりますか?
解決
まず、質問がどのようにユニーク以外にあるかを理解しているかどうかはわかりませんので、それを削除しようとすることを検討します。私はあなたのデータのロジックを知りませんので、それは適用できないかもしれませんが、それはあなたが避けることができるかもしれない余分なステップです。
これが私がそれにアプローチする方法です:
scope :answered, joins(:answers).order('answers.created_at desc')
scope :recent, take(10)
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")
@dogs_recently_answered = Question.answered.dogs.recent
@cats_recently_answered = Question.answered.dogs.recent
@mermaids_recently_answered = Question.answered.dogs.recent
これがシフトします TOP
クエリの一部は、フェッチするのではなく、属するデータベースへ すべて 行の、そして10を除くすべてを破棄します。あなたの団結した基準に応じて、あなたは次のようなスコープを使用するかもしれません
scope :unique, select('DISTINCT column_name')
そして、question.cats.unique.recentを使用して、データベースシステムが設計されているリレーショナル代数を活用する1つの高速クエリですべてを取得できます。
他のヒント
この場合、スコープが遅い理由は、3つの個別のデータベースクエリをもたらすため、他のアプローチは、3つの結果すべてが使用する単一のクエリによって満たされるという知識を使用しているためです。
それが事実であると仮定すると、システムがすぐに他のものを呼び出す最初のものをいつ呼び出すかわからないため、スコープが3つの別々のクエリを実行していることは驚くことではありません。このシナリオにとって賢明な最適化戦略があるかもしれませんが、ActiverCordがそれを実装することはわかりません。
とにかく、これはこの特定のケースの範囲の1つの欠点です。スコープはクリーン/クリアで柔軟で、クエリのために名前の抽象化をカプセル化しているため、スコープが好きです。 AFAICT、多くのシナリオでは、同等の直接クエリよりもそれほど遅くはありません。