Lingceng's Blog

Query Date Range With Ransack

The Traditional Way

Here I have a table of change records in my rails app. And I have added a query for created_at with ransack.

app/controllers/production_status_changes_controller.rb
1
2
3
4
5
6
class ProductionStatusChangesController < PlainController
  def index
    @q = ProductionStatusChange.ransack(params[:q])
    @orders = @q.result.includes(:order).page(params[:page]).per(params[:per])
  end
end
app/views/production_status_changes/index.html.erb
1
2
3
4
5
<%= search_form_for @q, url: production_status_changes_path, class: 'form-inline' do |f| %>
  <%=  f.label 'Create At' %>
  <%= f.search_field :created_at_gteq, class: 'form-control input-sm', 'datepicker' => true %>
  <%= f.search_field :created_at_lteq, class: 'form-control input-sm', 'datepicker' => true %>
<% end %>

The Problem

Everything works fine until users start to use it. They are surpised that, when query with “2015-01-01” and “2015-01-01”, nothing comes out.

Certainly there’s nothing between ‘2015-01-01 00:00’ and ‘2015-01-01 00:00’. But our users don’t think so. They shout that there is a whole day from 2015-01-01 to 2015-01-01!

Direct solution

OK. Users are gods. So I add some codes in my controller:

app/controllers/production_status_changes_controller.rb
1
2
3
4
5
6
7
8
def index
  params[:q] ||= {}
  if params[:q][:created_at_lteq].present?
    params[:q][:created_at_lteq] = params[:q][:created_at_lteq].to_date.end_of_day
  end
  @q = ProductionStatusChange.ransack(params[:q])
  @orders = @q.result.includes(:order).page(params[:page]).per(params[:per])
end

The created_at_lteq will convert to ‘2015-01-01 23:59’.

DRY

I customed the ransack predicates to avoid duplication. So I can just write the view like following:

Maybe Another Way

Maybe we can change the js datepicker to set time to 59:59 by default. I use bootstrap-datetimepicker. I find the javascript solution:

= hidden_field_tag 'q[created_at_lteq]', params[:q].try(:[], :created_at_lteq)
= date_field_tag '', params[:q].try(:[], :created_at_lteq).to_s.sub(/ .+/, ''), onchange: "$(this).prev().val($(this).val() != '' ? $(this).val() + ' 23:59:59' : '')"

One Thing More

Following make query in one day more convenient.

1
2
3
4
5
6
7
class ProductionStatusChange < ActiveRecord::Base
  ransacker :created_on do
    Arel.sql("DATE(#{table_name}.created_at)")
  end
end

@q = ProductionStatusChange.ransack(created_on: '2016-01-01')