Class: DbTextSearch::CaseInsensitive

Inherits:
Object
  • Object
show all
Defined in:
lib/db_text_search/case_insensitive.rb,
lib/db_text_search/case_insensitive/lower_adapter.rb,
lib/db_text_search/case_insensitive/abstract_adapter.rb,
lib/db_text_search/case_insensitive/collate_nocase_adapter.rb,
lib/db_text_search/case_insensitive/insensitive_column_adapter.rb

Overview

Provides case-insensitive string-in-set querying, LIKE querying, and CI index creation.

Defined Under Namespace

Classes: AbstractAdapter, CollateNocaseAdapter, InsensitiveColumnAdapter, LowerAdapter

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scope, column) ⇒ CaseInsensitive

Returns a new instance of CaseInsensitive.

Parameters:

  • scope (ActiveRecord::Relation, Class<ActiveRecord::Base>)
  • column (Symbol)

    name



12
13
14
15
# File 'lib/db_text_search/case_insensitive.rb', line 12

def initialize(scope, column)
  @adapter = self.class.adapter_class(scope.connection, scope.table_name, column).new(scope, column)
  @scope   = scope
end

Class Method Details

.adapter_class(connection, table_name, column_name) ⇒ Class<AbstractAdapter>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter)
  • table_name (String, Symbol)
  • column_name (String, Symbol)

Returns:



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/db_text_search/case_insensitive.rb', line 80

def self.adapter_class(connection, table_name, column_name)
  lower_or_insensitive = -> {
    column_case_sensitive?(connection, table_name, column_name) ? LowerAdapter : InsensitiveColumnAdapter
  }
  DbTextSearch.match_adapter(
      connection,
      mysql:    lower_or_insensitive,
      postgres: lower_or_insensitive,
      # Always use COLLATE NOCASE for SQLite, as we can't check if the column is case-sensitive.
      # It has no performance impact apart from slightly longer query strings for case-insensitive columns.
      sqlite:   -> { CollateNocaseAdapter }
  )
end

.add_ci_text_column(connection, table_name, column_name, options = {}) ⇒ Object

Adds a case-insensitive column to the given table.

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter)
  • table_name (String, Symbol)
  • column_name (String, Symbol)
  • options (Hash) (defaults to: {})

    passed to ActiveRecord::ConnectionAdapters::SchemaStatements#add_index



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/db_text_search/case_insensitive.rb', line 44

def self.add_ci_text_column(connection, table_name, column_name, options = {})
  type, options = DbTextSearch.match_adapter(
      connection,
      mysql:    -> { [:text, options] },
      postgres: -> {
        connection.enable_extension 'citext'
        [(ActiveRecord::VERSION::STRING >= '4.2.0' ? :citext : 'CITEXT'), options]
      },
      sqlite:   -> {
        if ActiveRecord::VERSION::MAJOR >= 5
          [:text, options.merge(collation: 'NOCASE')]
        else
          ['TEXT COLLATE NOCASE', options]
        end
      }
  )
  connection.add_column table_name, column_name, type, **options
end

.add_index(connection, table_name, column_name, options = {}) ⇒ Object

Add an index for case-insensitive string search.

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter)
  • table_name (String, Symbol)
  • column_name (String, Symbol)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • name (String)

    index name

  • unique (Boolean)

    default: false



71
72
73
# File 'lib/db_text_search/case_insensitive.rb', line 71

def self.add_index(connection, table_name, column_name, options = {})
  adapter_class(connection, table_name, column_name).add_index(connection, table_name, column_name, options)
end

.column_case_sensitive?(connection, table_name, column_name) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

sqlite not supported.

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter)
  • table_name (String, Symbol)
  • column_name (String, Symbol)

Returns:

  • (Boolean)


100
101
102
103
104
105
106
107
108
109
# File 'lib/db_text_search/case_insensitive.rb', line 100

def self.column_case_sensitive?(connection, table_name, column_name) # rubocop:disable Metrics/AbcSize
  column = connection.schema_cache.columns(table_name).detect { |c| c.name == column_name.to_s }
  fail "Column #{column_name.to_s.inspect} not found on table #{table_name.inspect}" if column.nil?
  DbTextSearch.match_adapter(
      connection,
      mysql:    -> { column.case_sensitive? },
      postgres: -> { column.sql_type !~ /citext/i },
      sqlite:   -> { DbTextSearch.unsupported_adapter! connection }
  )
end

Instance Method Details

#column_for_order(asc_or_desc) ⇒ Arel::Collectors::SQLString

Returns a string to be used within an ‘.order()`.

Parameters:

  • asc_or_desc (Symbol)

Returns:

  • (Arel::Collectors::SQLString)

    a string to be used within an ‘.order()`



34
35
36
37
# File 'lib/db_text_search/case_insensitive.rb', line 34

def column_for_order(asc_or_desc)
  fail 'Pass either :asc or :desc' unless i[asc desc].include?(asc_or_desc)
  @adapter.column_for_order(asc_or_desc)
end

#in(value_or_values) ⇒ ActiveRecord::Relation

Parameters:

  • value_or_values (String, Array<String>)

Returns:

  • (ActiveRecord::Relation)


19
20
21
22
23
# File 'lib/db_text_search/case_insensitive.rb', line 19

def in(value_or_values)
  values = Array(value_or_values)
  return @scope.none if values.empty?
  @adapter.in(values)
end

#prefix(query) ⇒ ActiveRecord::Relation

Returns the scope of records with matching prefix.

Parameters:

  • query (String)

Returns:

  • (ActiveRecord::Relation)

    the scope of records with matching prefix.



27
28
29
30
# File 'lib/db_text_search/case_insensitive.rb', line 27

def prefix(query)
  return @scope.none if query.empty?
  @adapter.prefix(query)
end