我需要在rails中的列上创建一个不区分大小写的索引。我是通过SQL做到的:
execute(
"CREATE UNIQUE INDEX index_users_on_lower_email_index
ON users (lower(email))"
)
这很好用,但在我的schema.rb文件中我有:
add_index "users", [nil],
:name => "index_users_on_lower_email_index",
:unique => true
注意“无”。因此,当我尝试克隆数据库以运行测试时,我得到一个明显的错误。我在这里做错了吗?我是否应该在rails中使用其他约定?
感谢您的帮助。
答案 0 :(得分:35)
由于MySQL索引已经不区分大小写,我猜你正在处理PostgreSQL,它默认会创建区分大小写的索引。我在这里回答基于Rails 3.2.3和PostgreSQL 8.4。
似乎functional indexes是ActiveRecord无法生成的事物的又一个例子。外键和UUID列是另外两个想到的。因此,除了使用猴子修补ActiveRecord之外别无选择,只能使用execute
语句。
这意味着为了准确转储数据库,您需要放弃与数据库无关的schema.rb,转而使用特定于DB的structure.sql。请参阅“迁移Rails指南”部分6.2 Types of Schema Dumps。设置如下:
<强>配置/ application.rb中强>
config.active_record.schema_format = :sql
运行迁移时,应自动更新db / structure.sql。您可以使用以下命令手动生成它:
rake db:structure:dump
该文件是纯Postgres SQL。虽然在使用rake -T
列出rake任务时没有记录,但似乎可以使用此命令从structure.sql dump加载数据库:
rake db:structure:load
这里没有什么神奇之处:source code(在Rails 3.2.16中显示)只是在structure.sql上调用psql。
最后,我的迁移是删除旧的,区分大小写的电子邮件约束并添加区分大小写的功能索引:
class FixEmailUniqueIndexOnUsers < ActiveRecord::Migration
def up
remove_index :users, :email
execute "CREATE UNIQUE INDEX index_users_on_lowercase_email
ON users USING btree (lower(email));"
end
def down
execute "DROP INDEX index_users_on_lowercase_email;"
add_index :users, :email, :unique => true
end
end
答案 1 :(得分:24)
如果您使用的是PostgreSQL,则可以将列类型更改为citext
- 不区分大小写的字符串。它还使搜索独立于寄存器。
def change
enable_extension :citext
change_column :users, :email, :citext
add_index :users, :email, unique: true
end
答案 2 :(得分:6)
我会简化这个......
在你的模特中:
before_validation :downcase_email
def downcase_email
self.email = email.downcase
end
这样,索引与数据库无关,您的电子邮件在数据库中都是小写的。
答案 3 :(得分:4)
您是否考虑过使用schema_plus
(https://github.com/lomba/schema_plus)?除了支持在数据库和视图中强制执行外键之外,它还支持为PostgreSQL数据库设置不区分大小写的索引,并处理它们在模式中的转储。从自述文件“如果您正在使用Postgresql,SchemaPlus提供对条件,表达式,索引方法和不区分大小写的索引的支持。”
答案 4 :(得分:2)
documentation目前尚不清楚如何做到这一点,但来源如下:
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
因此,如果您的数据库quote_column_name
是默认实现(根本不执行任何操作),那么这可能会有效:
add_index "users", ['lower(email)'], :name => "index_users_on_lower_email_index", :unique => true
你注意到你尝试了那个,但它没有用(将它添加到你的问题可能是一个好主意)。看起来ActiveRecord根本不了解计算值的索引。我可以想到一个丑陋的黑客将会完成它,但它很难看:
email_lc
列。before_validation
或before_save
个钩子,将email
的小写版本放入email_lc
。email_lc
。这很丑陋,你可能觉得这样做很脏,但这是我现在能想到的最好的。
答案 5 :(得分:0)
我建议(仅作为一个考虑其他人的机会)使用两个单独的字段:
第一个始终是向下的,因此可以以数据库无关的方式进行唯一索引,而第二个字段逐字逐句地保持用户的兴趣并且可以具有高位字符。
显然,在用户模型中,需要在每次保存时根据email_original设置电子邮件,并禁止使用电子邮件字段进行直接修改。
答案 6 :(得分:-1)
对于Rails 4.2,在name列的users表中创建不区分大小写的唯一索引。
使用空更改方法创建新的迁移文件:
$ rails generate migration add_index_in_users_on_name
添加add_index方法调用以清空更改方法:
add_index :users, 'lower(name)', name: 'index_users_on_lower_name', unique: true
运行Rake db:migrate任务:
$ rake db:migrate
结果,将正确添加索引,文件db / schema.rb包含正确的add_index:
add_index "users", ["LOWER(\"NAME\")"], name: "index_users_on_lower_name", unique: true
仅针对RDB Oracle进行了测试。
答案 7 :(得分:-2)
我认为你需要列名如下
add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }
并且您必须使用正确的Case Insensitive排序规则在数据库中创建电子邮件字段,这样您的索引也将不区分大小写。
取决于您正在使用的数据库引擎,语法可能不同,但
alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...
当您向此列添加索引时,它将不区分大小写。