Case-insensitive unique index in Rails/ActiveRecord?

前端 未结 8 1606
天命终不由人
天命终不由人 2020-12-09 03:38

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         


        
相关标签:
8条回答
  • 2020-12-09 03:46

    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."

    0 讨论(0)
  • 2020-12-09 03:52

    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.

    0 讨论(0)
  • 2020-12-09 04:00

    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:

    1. Add an email_lc column.
    2. Add a before_validation or before_save hook to put a lower case version of email into email_lc.
    3. Put your unique index on 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.

    0 讨论(0)
  • 2020-12-09 04:04

    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.

    0 讨论(0)
  • 2020-12-09 04:07

    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
    
    0 讨论(0)
  • 2020-12-09 04:07

    I would suggest (just as an opportunity to consider among others) to use two separate fields:

    • email
    • email_original

    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.

    0 讨论(0)
提交回复
热议问题