1. ホーム
  2. ruby-on-rails

ActiveRecord 日付フィールドの年、日、月による検索

2023-11-03 22:01:33

質問

ActiveRecordのモデルで、日付属性を持っています。 その日付属性を利用して、年、日、月で検索することは可能でしょうか。

Model.find_by_year(2012)
Model.find_by_month(12)
Model.find_by_day(1)

とか、単純にfind_by_date(2012-12-1)でいいのでしょうか。

年、月、日の属性を作らなくて済むと思ったのですが。

どのように解決するのですか?

date 属性が日付(タイムスタンプではなく)であると仮定して、単純な where を実行すると、日付で検索することができます。

Model.where(:date_column => date)

あなたが必要としない find_by_date_column は必要ありません。

年、月、日のクエリには extract SQL関数を使用します。

Model.where('extract(year  from date_column) = ?', desired_year)
Model.where('extract(month from date_column) = ?', desired_month)
Model.where('extract(day   from date_column) = ?', desired_day_of_month)

しかし、SQLite を使っているのであれば、このように strftime が何であるかを知らないので extract が何であるかを知らないからです。

Model.where("cast(strftime('%Y', date_column) as int) = ?", desired_year)
Model.where("cast(strftime('%m', date_column) as int) = ?", desired_month)
Model.where("cast(strftime('%d', date_column) as int) = ?", desired_day_of_month)

%m%d の書式指定子は、場合によっては先頭のゼロを追加してしまい、等値性テストを混乱させる可能性があるので、そのため cast(... as int) で強制的に数字に変換しています。

ActiveRecordはデータベース間のすべての違いからあなたを守ってはくれません。したがって、非自明なことをするとすぐに、独自の移植性レイヤーを構築するか(醜いが、時には必要)、限られたデータベースセットに自分を縛るか(あらゆるデータベース上で実行しなければならないものをリリースしていない限り現実的)、すべての論理をRubyで行う(非自明な量のデータに対しては非常識)必要があります。

年、月、および月初のクエリは、大きなテーブルではかなり遅くなります。いくつかのデータベースでは、関数の結果にインデックスを追加できますが、ActiveRecordはそれを理解するにはあまりに愚かなので、それを使おうとすると大きな混乱を引き起こします。

これらのクエリを頻繁に使用するのであれば、スコープを追加することができます。 引数でスコープを追加する推奨方法 はクラスメソッドを追加するだけです。

クラスメソッドを使用することは、スコープの引数を受け取るための好ましい方法です。これらのメソッドはまだアソシエーションオブジェクト上でアクセス可能です...

つまり、次のようなクラスメソッドを持つことになります。

def self.by_year(year)
    where('extract(year from date_column) = ?', year)
end
# etc.