[Rails] ransackを使った日時範囲検索

学習記録

はじめに

掲示板一覧画面に、作成日を「◯◯月◯◯日〜◯◯月◯◯日」のような感じで日時範囲検索できるような機能を実装していきます。

前提としてgemのransackは導入してあるものとします。

作業の流れ

  • コントローラの作成
  • ransackの検索条件を調べる
  • 検索フォームに実装する

コントローラの作成

def index
  @search = Board.ransack(params[:q])
  @boards = @search.result(distinct: true).includes(:user).order(created_at: :desc).page(params[:page])
end

使えそうなransackの検索条件を調べる

今回は「◯◯月◯◯日〜◯◯月◯◯日」のような検索条件を利用したいです。どのようにしたらよいでしょうか?

まず実現可能なpredicateが用意されているか確認します。

predicateとは

ransackを使った検索フォームで使用できる検索述語のことです。

例えばconteqのようなものです。

公式wikiにまとめられています。こちらから今回の実装にあたって使えそうなものを探しましょう。

既存のpredicate _gteq と_lteq

使えそうなpredicateが2つ見つかりました。

_gteq greater than or equal(より大きいか等しい)

_lteq less than or equal(より小さいまたは等しい)

のように使えそうですね。さっそくこちらを使って実装していきましょう!

検索フォームに実装する

<%= search_form_for @search, url: admin_boards_path do |f| %>
  <div class="row">
    <div class="form-inline align-items-center mx-auto">
      <div class="col-auto">
        <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
      </div>
      <div class="col-auto">
        <%= f.date_field :created_at_gteq, class: 'form-control' %>
        <span>~</span>
        <%= f.date_field :created_at_lteq, class: 'form-control' %>
      </div>
      <div class="col-auto">
        <%= f.submit class: 'btn btn-primary' %>
      </div>
    </div>
  </div>
<% end %>

f.date_field は日付の入力欄を生成させます。

画像のように検索条件を「2021/10/01 ~ 2021/10/06」としました。

https://i.gyazo.com/af8a23580986f1093905b3f7a41d4794.png

しかし「2021/10/06」に作成された掲示板が表示されませんでした。おかしいですね。

発行されたSQLを見ていきます。

(0.1ms)  SELECT COUNT(DISTINCT "boards"."id") FROM "boards" WHERE ("boards"."created_at" >= '2021-10-01 00:00:00' AND "boards"."created_at" <= '2021-10-06 00:00:00')

どうやら実際に検索された条件は「’2021-10-01 00:00:00′ から ‘2021-10-06 00:00:00まで」だったようです。

つまり、「2021/10/06」の前日分(2021/10/05)までしか検索されていなかったのです!

ransackの検索条件をカスタム

さきほどの問題を解決すべく、ransackのpredicateをカスタムしましょう。公式wikiを参考にします。

Ransack.configure do |config|
  config.add_predicate 'equals_diddly',# 述語に名前をつけます。
    # どんな非複合ARel述語を使うのか?(eq, matches, etc.)
    arel_predicate: 'eq',
    # 入力される値を好きなようにフォーマットします。(デフォルト: フォーマットを行わない)
    formatter: proc { |v| "#{v}-diddly" },
end

config/initializars/ransack.rbに設定しましょう。ファイルが無ければ、作成してください。

Ransack.configure do |config|
  config.add_predicate 'lteq_end_of_day',          # lteq_end_of_dayという述語の名前をつける
                  arel_predicate: 'lteq',          # カスタムの元にするpredicateを指定
                       formatter: proc { |v| v.end_of_day }  # ここでend_of_dayメソッドを実行している
end
  • arel_predicate:でカスタムする述語を指定します。
  • procとはなにか?

ざっくり言えば、ブロックをオブジェクト化するのがprocです。

今回で言えば、{ |v| v.end_of_day } このブロックの処理をひとかたまりとしてprocに落とし込んでいるのです。

参考:https://docs.ruby-lang.org/ja/latest/class/Proc.html

  • end_of_dayメソッド

end_of_dayメソッドは、その日の最後の時点 (23:59:59) のタイムスタンプを返します。

参考:https://railsguides.jp/active_support_core_extensions.html#beginning-of-day、end-of-day

irb(main):001:0> now = Time.current
=> Sun, 17 Oct 2021 18:51:21 JST +09:00
irb(main):002:0> now = Time.current.end_of_day
=> Sun, 17 Oct 2021 23:59:59 JST +09:00

検索フォームにカスタムしたpredicateを実装する

気を取り直して再び検索フォームを開いてカスタムしたpredicateを実装するように修正しましょう。

<%= search_form_for @search, url: admin_boards_path do |f| %>
  <div class="row">
    <div class="form-inline align-items-center mx-auto">
      <div class="col-auto">
        <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
      </div>
      <div class="col-auto">
        <%= f.date_field :created_at_gteq, class: 'form-control' %>
        <span>~</span>
        <%= f.date_field :created_at_lteq_end_of_day, class: 'form-control' %>
      </div>
      <div class="col-auto">
        <%= f.submit class: 'btn btn-primary' %>
      </div>
    </div>
  </div>
<% end %>

次はしっかり期待通りの挙動が得られました。発行されたSQLを見ておきましょう。

(0.2ms)  SELECT COUNT(DISTINCT "boards"."id") FROM "boards" WHERE ("boards"."created_at" >= '2021-10-01 00:00:00' AND "boards"."created_at" <= '2021-10-06 23:59:59.999999')

参考記事

GitHub - activerecord-hackery/ransack: Object-based searching.
Object-based searching. . Contribute to activerecord-hackery/ransack development by creating an account on GitHub.
GitHub - activerecord-hackery/ransack: Object-based searching.
Object-based searching. . Contribute to activerecord-hackery/ransack development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました