Edited at

ActiveRecord の where でレコードを直接絞れないケースでも絞り込む方法を調べた


はじめに

Rails でアプリ開発をする際、モデルのレコードを where で絞り込むケースはよくあると思いますが、where で直接絞れないケースで絞り込む必要があり、その方法について調べました。


課題

例として、Post モデルを考えます。Post モデルには、title と、API からのレスポンス json を保持しておく raw_json カラムがあります。作成するときはこんなイメージです。

Post.create(

title: 'テスト1',
raw_json: '{"date": "2019-08-01"}'
)

Post.create(
title: 'テスト2',
raw_json: '{"date": "2019-08-02"}'
)

この raw_json カラムの date の日付でデータを絞り込む必要があったのですが、この方法について調べていました。


tl;dr

こんなスコープを作りました。

scope :filter_by_tartet_date, -> (target_date) {

return if !target_date.present?
ids = self.select do |s|
if date = JSON.parse(s.raw_json)['date']
Date.parse(date) == Date.parse(target_date)
end
end.map(&:id)
where(id: ids)
}

使い方としては、Post.filter_by_tartet_date('2019-08-01') という感じで日付文字列を引数に指定すると、raw_json 内の date2019-08-01 のレコードに絞り込んでくれます。


解説

ざっくりしたイメージとしては以下のようになります。



  • select メソッドで raw_json 内の値を使って比較


  • map メソッドで対象レコードの id のみの配列を作成


  • where メソッドで対象 id のレコードのみ絞り込んで ActiveRecord::Relation クラスの戻り値に

select メソッドを使うだけだと、戻り値が Array クラスになってしまい、ページネーションなどの使い勝手が悪かったため、最後に where メソッドで ActiveRecord::Relation クラスの戻り値に変換して使いやすくしています。


おわりに

直接 where メソッドで絞れない場合でも、Array クラスのメソッドを挟んで絞る方法が使えました。直感的に書ける ruby はやっぱり便利だなと感じます。

他にもいい方法ありましたら教えていただけるとありがたいです。


参考