I need to create a case-insensitive index on a column in rails. I did this via SQL:
execute(
\"CREATE UNIQUE INDEX index_users_on_lower_email_index
O
Have you considered using schema_plus
(https://github.com/lomba/schema_plus)? Among other things (support for enforcing foreign keys in the database and for views), it supports setting case-insensitive indexes for PostgreSQL databases and handles dumping them in the schema. From the Readme, "If you’re using Postgresql, SchemaPlus provides support for conditions, expressions, index methods, and case-insensitive indexes."
I think you need column name(s) as below
add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }
And you have to make email field in database with proper Case Insensitive collation, that way your index will be also case insensitive.
depending of db engine which you are using it syntax may be different but
alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...
and when you add index to this column it will be case insensitive.
The documentation is unclear on how to do this but the source looks like this:
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
end
So, if your database's quote_column_name
is the default implementation (which does nothing at all), then this might work:
add_index "users", ['lower(email)'], :name => "index_users_on_lower_email_index", :unique => true
You note that you tried that one but it didn't work (adding that to your question might be a good idea). Looks like ActiveRecord simply doesn't understand indexes on a computed value. I can think of an ugly hack that will get it done but it is ugly:
email_lc
column.before_validation
or before_save
hook to put a lower case version of email
into email_lc
.email_lc
.That's pretty ugly and you might feel dirty for doing it but that's the best I can think of right now.
For Rails 4.2, create case-insensitive unique index in users table on name column.
Create new migration file with empty change method:
$ rails generate migration add_index_in_users_on_name
Add call of add_index method to empty change method:
add_index :users, 'lower(name)', name: 'index_users_on_lower_name', unique: true
Run Rake db:migrate task:
$ rake db:migrate
In result, index will be added correctly and file db/schema.rb contains correct add_index:
add_index "users", ["LOWER(\"NAME\")"], name: "index_users_on_lower_name", unique: true
This tested only with RDB Oracle.
If you are using PostgreSQL you can change your column type to citext
- case-insensitive string. It also makes search independent from the register.
def change
enable_extension :citext
change_column :users, :email, :citext
add_index :users, :email, unique: true
end
I would suggest (just as an opportunity to consider among others) to use two separate fields:
The first one is always down cased and thus can be uniquely indexed in a database agnostic manner, while the second field keeps verbatim to what user enetered and can have upper characters.
Obviously in User model one then needs to set email based on email_original on each save and prohibit direct mangling with email field.