Search code examples
ruby-on-railscase-insensitiveransack

Ransack gem - Case insensitive search


Using Ransack gem, I wanna to do like this

https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching#i_cont-work-in-progress-dont-use-yet

 >> User.search(first_name_i_cont: 'Rya').result.to_sql
=> SELECT "users".* FROM "users"  WHERE (UPPER("users"."first_name") LIKE UPPER('%Rya%'))

but this method not work yet.

so I tried to do figure out , is there other way can do it I got some information about how to do

//in model

  ransacker :ig_case, formatter: proc { |v| v.mb_chars.upcase.to_s } do |parent|
    Arel::Nodes::NamedFunction.new('UPPER',[parent.table[:firstname]])
  end

//in config/ranrack.rb

Ransack.configure do |config|
  config.add_predicate 'ig_case', # Name your predicate
    arel_predicate: 'matches',
    formatter: proc { |v| "%#{v.to_s.gsub(/([\\|\%|.])/, '\\\\\\1').mb_chars.upcase}%"},
    validator: proc { |v| v.present? },
    compounds: true,
    type: :string
end

   // use way
User.search({ firstname_or_lastname_ig_case: "ABC"}).result.to_sql

=> "SELECT `Users`.* FROM `Users` WHERE ((UPPER(`users`.`firstname`) LIKE '%ABC%' OR (`users`.`lastname`) LIKE '%ABC%'))"

after few hours, I found I just can get upper case one field each time when I used in model way.

I can upcase all field if I choice in config way but I can't get sql like this 'UPPER("users"."first_name")'

Is there any solution? I really really thanks.


Solution

  • You'll need to override Arel in the adapters by doing the following:

        module Arel
    
          module Nodes
            %w{
              IDoesNotMatch
              IMatches
            }.each do |name|
              const_set name, Class.new(Binary)
            end
          end
    
          module Predications
            def i_matches other
              Nodes::IMatches.new self, other
            end
    
            def i_does_not_match other
              Nodes::IDoesNotMatch.new self, other
            end
          end
    
          module Visitors
    
            class ToSql < Arel::Visitors::Visitor
              def visit_Arel_Nodes_IDoesNotMatch o
                "UPPER(#{visit o.left}) NOT LIKE UPPER(#{visit o.right})"
              end
    
              def visit_Arel_Nodes_IMatches o
                "UPPER(#{visit o.left}) LIKE UPPER(#{visit o.right})"
              end
            end
    
            class Dot < Arel::Visitors::Visitor
              alias :visit_Arel_Nodes_IMatches            :binary
              alias :visit_Arel_Nodes_IDoesNotMatch       :binary
            end
    
            class DepthFirst < Visitor
    
              unless method_defined?(:visit_Arel_Nodes_InfixOperation)
                alias :visit_Arel_Nodes_InfixOperation :binary
                alias :visit_Arel_Nodes_IMatches            :binary
                alias :visit_Arel_Nodes_IDoesNotMatch       :binary
              end
    
            end
    
          end
        end
    

    In addition to this, you'll need to provide the methods for the predications.

    Here's my fork of the gem that solves your issue: https://github.com/Kartstig/ransack

    I have a PR that got closed because it may have been breaking other adapters It has been working greate in my app so far: https://github.com/activerecord-hackery/ransack/pull/405

    Also note, if you have any indexed columns, they will be ignored because you are using UPPER.