ボクココ

個人開発に関するテックブログ

Rails Active Record における rewhere の使い所

ども、@kimihom です。

f:id:cevid_cpp:20191121161954j:plain

今回は ActiveRecord でもおそらくマイナーなメソッドであろう rewhere を使う機会があったのでメモとして残しておく。

今回の利用ケース

とあるデータの取得条件でフィルタリングをして統計として表示するページを想像してみて欲しい。そこではあらゆる where 条件で絞り込みを指定している。そしてもちろん取得する件数や並びの順番なども指定している。

# 今日更新された特定の会社に所属するコンタクトを作成日時の新しい順で30件
@records = Contact.where(
  "updated_at between ? and ?",
  DateTime.now.beginning_of_day, 
  DateTime.now.end_of_day
)
@records = @records.where(company_id: company.id)
@records = @records.order("created_at desc").limit(30)

さて、上記では今日のコンタクトのみとして絞り込んでいるが、日毎の比較をしたいケースが出てくるだろう。上記のデータ取得と全く同じ条件で「昨日」という時期だけが違う時に、どうすれば良いのだろう。そこで、まず以下のコードを書くと思い通りに動作しない。

@yesterday_records = @records.where(
  "updated_at between ? and ?",
  DateTime.yesterday.beginning_of_day,
  DateTime.yesterday.end_of_day
)
#=> WHERE updated_at between "今日0:00" and "今日0:00" 
#     AND updated_at between "明日0:00" and "明日0:00"

当然これにマッチする結果が取ってこれずに、0件の結果として返ってきてしまう。仕方ないから、Contact.where でゼロから書き直すというのがよくある解決策だろう。でも Rails を使っていて、そんなことはしたくないよね。

Rails ならそんなことはさせないようにメソッドが提供されているはずだと思って調べたところ rewhere メソッドを見つけた。rewhere を使うと、既にセットされた ActiveRecord の一致する条件を上書きした where として定義できる。私が探していたのはこれだった。先ほどのコードに以下を追記するだけでよくなる。

# 昨日更新された特定の会社に所属するコンタクトを作成日時の新しい順で30件
@yesterday_records = @records.rewhere(
  "updated_at between ? and ?",
  DateTime.yesterday.beginning_of_day,
  DateTime.yesterday.end_of_day
)

素晴らしい・・!これで、今日と昨日の比較を簡単に表示できるようになった。

詳細ドキュメント rewhere (ActiveRecord::QueryMethods) - APIdock

終わりに

今回は Rails Active Record における rewhere メソッドの使いどきについて記した。

Rails では特に、「~がしたいんだけどなぁ」ってケースが出てきた際、自前で Ruby でゴリゴリ書く前に、まず Rails のドキュメントを参照しよう。調べると案外、その用途にマッチしたメソッドってのが出てくる。それだけで、何十行ものコードと余計な時間を節約することができる。

自分で実装するより、調べて答えを見つける。 Rails を使う場合には特に必要な意識づけになる。