Luke
Luke

Reputation: 95

Ransack gem - Case insensitive sort (Rails 4)

Using the Ransack gem, I'm trying to sort a simple list on an index page. However, there are both uppercase and lowercase values in the sort field name. To make them sorted useful, the sort should be case insensitive.

This is the model Vendor:

name:string, email:string, address:string, phone:string, fax:string

This is the controller:

class VendorsController < ApplicationController

def index
  @search = Vendor.search(params[:q])
  @vendors = @search.result
end

And the view:

/index.html.slim
tr
  th = sort_link @search, :name, "Name"
  th = sort_link @search, :email, "Company Email"
  th = sort_link @search, :address, "Address"

Any advice would be greatly appreciated.

Upvotes: 4

Views: 3868

Answers (6)

NGobin
NGobin

Reputation: 403

RobDil's answer from 2016 is now recognized as the best way to do this for multiple columns on a model in the ransack documentation. But if you want to do this for just one column, you can do it with a custom ransacker:

class Vendor < ApplicationRecord

  ransacker :name, type: :string do
    Arel.sql("lower(vendors.name)")
  end

end

Note that there are a couple nuances now vs. mutexkid's 2014 answer:

  • you must specify the table name when using the Arel.sql() syntax
  • you can simply override the "name" default ransacker, rather than creating a separate name_case_insensitive one. Searching still works fine. No need to change sort links in your views.

If you do want to create a separate name_case_insensitive ransacker, you will need to add "name_case_insensitive" to your ransackable_attributes for the Vendor object.

Credit to RobDil and mutexkid for the original answers, just thought I'd add this here as figuring out the syntax specifics took me way too long a decade after they were published.

Upvotes: 1

Thuy Nguyen
Thuy Nguyen

Reputation: 362

Ransack now has a case_insensitive option you can custom a predicate with case_insensitive: false

Refrences: https://github.com/activerecord-hackery/ransack/wiki/Custom-Predicates

Upvotes: 2

RobDil
RobDil

Reputation: 4544

You can solve this for all string columns of a model at once by using this approach:

# lib/ransack_object.rb

module RansackObject

  def self.included(base)
    base.columns.each do |column|
      if column.type == :string
        base.ransacker column.name.to_sym, type: :string do
          Arel.sql("lower(#{base.table_name}.#{column.name})")
        end
      end
    end
  end

end

Then include the ransack object in your model:

class UserWithManyAttributes < ActiveRecord::Base

  include RansackObject

end

Upvotes: 3

Claudio Ikeda
Claudio Ikeda

Reputation: 121

10 months, maybe too late!!

This solved my problem with posgresql

sort_link @search, 'lowercase(name)', "Name"

Upvotes: 1

mutexkid
mutexkid

Reputation: 1084

What you're looking for is a custom "Ransacker":

class Vendor < ActiveRecord::Base

    ransacker :name_case_insensitive, type: :string do
      arel_table[:name].lower
    end

end

view:

th = sort_link(@q, :name_case_insensitive)

Upvotes: 7

Luke
Luke

Reputation: 95

Thanks to hd1 who posted the approach above. I am posting the actual code here - it's not ideal, since it seems like a wordy workaround. Still open to cleaner suggestions.

def index
  @search = Vendor.search(params[:q])
  @vendors = @search.result

  if params[:q].present?
    params[:q].each do |k, v| 
      if v == 'name asc'
        @vendors = @search.result.sort { |p1, p2| p1.name.downcase <=> p2.name.downcase }
      elsif v == 'name desc'
        @vendors = @search.result.sort { |p2, p1| p1.name.downcase <=> p2.name.downcase }
      end
    end
  end
end

Upvotes: 0

Related Questions