はじめに
Railsのモデルでよくscopeを使用した記述をします。
scopeの使い方が悪く、予期せぬクエリが呼ばれていたので備忘録として残します。
良くなかった書き方
モデル名やscope名は仮で入れています。
class Model
scope :prev_staff_id, ->(customer_id) {
where(customer_id: customer_id)
.order(updated_at: :desc)
.limit(1)
.pick(:staff_id)
}
end
Model.prev_staff_idとすれば、以前のスタッフIDを返すことを期待しています。
limit(1)を指定して、staff_idを指定しているので一見クエリに問題はなさそうです。
問題点
①メソッドチェーンできるようにしておく
メソッドチェーンは今回のprev_staff_idに加えて、activeのような別のスコープが合ったときに以下のように常ゲテ使用することを言います。
Model.prev_staff_id.active
ですが、prev_staff_idの返り値はstaff_idのため、そもそもscopeの使い方として適しておりませんでした。
②思わぬクエリが呼ばれていた
今回の失態に気づくきっかけにもなったのですが、アプリの読み込みに想定以上の時間がかかっておりました。
よくログを見てみると、ある状態で操作するとあるモデルの全件取得クエリが確認できました。
デバッグすると例示しているscopeに問題があるとわかり、scopeを誤った使い方をするとlimitされずに全件取得のクエリが走っていたのでした。
対処法
いくつか対応の方法はありますがクラスソッドに変更するのが無難なのではないでしょうか。
class Model
def self.prev_record(customer_id)
where(customer_id: customer_id)
.order(updated_at: :desc)
.first
end
end
prev_staff_id = Mode.prev_record.staff_id
まとめ
- クラスソッド、scopeの使い分けを考えて実装する必要がある
- 誤った使い方をすると想定しないクエリが発生してしまうので注意が必要である
- 問題が起きたときはログを確認して確実に対処していく