はじめに
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の使い分けを考えて実装する必要がある
- 誤った使い方をすると想定しないクエリが発生してしまうので注意が必要である
- 問題が起きたときはログを確認して確実に対処していく