sqlite3 on rails: create_table using collate nocas

2020-04-23 05:57发布

问题:

Using rails 4.2.0 with ruby v2.3.0p0

I want to create indexes that are case insensitive since most of my searches are case insensitive and I don't want to have to do a full table search every time.

So in my create_table migrations, I try adding lines such as:

add_index :events, :name, :COLLATE => :NOCASE

And when I migrate I get:

== 20150515163641 CreateEvents: migrating =====================================
-- create_table(:events)
   -> 0.0018s
-- add_index(:events, :submitter, {:COLLATE=>:NOCASE})
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

Unknown key: :COLLATE. Valid keys are: :unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type

How can I create a case-insensitive index in my migration files using SQLITE3 and rails?

UPDATE ------------------------

Thanks to Anthony's help, I see that rails doesn't support collate/collation in the Sqlite3 code yet.

So I tried writing it manually in my migration based on the syntax that Rails would generate, but with the collate option added:

execute("CREATE INDEX 'index_events_on_name' ON 'events' ('name' COLLATE NOCASE);")

This creates the index properly, according to Sqlite3, but the Schema.rb is now wrong, of course.

And more importantly, it still doesn't seem to be doing case-insensitive comparisons when I search:

Event.where("name == ?",name)
Event.find_by_name(name)

How do I convince rails to use the index I created?

回答1:

Doesn't look like collation on an index has been merged yet:

https://github.com/rails/rails/pull/18499/files

This PR, adds collation support for columns by the way.

The syntax looks like:

create_table :foo do |t|
  t.string :string_nocase, collation: 'NOCASE'
  t.text :text_rtrim, collation: 'RTRIM'
end

add_column :foo, :title, :string, collation: 'RTRIM'

change_column :foo, :title, :string, collation: 'NOCASE'


回答2:

For people searching, if 'collate' is still not available in your version of rails (you'll get "Unknown key: :COLLATE"), then you have to create the index manually:

execute("CREATE INDEX 'index_events_on_name' ON 'events' ('name' COLLATE NOCASE);")

Then to search using the index, you can do:

Event.where("name collate nocase == ?",name)
Event.where("name LIKE ?",name)

The 'find_by_name' won't be case insensitive. I believe you can do this by putting the 'collate nocase' on the create table instead of in the index (this will also need to use an "execute()" call. This means that you will always be forced to match case-insensitive, i.e., this will also be case-insensitive:

Event.where("name == ?",name)