Rails/ActiveRecord中不区分大小写的唯一索引?

Bin*_*gic 22 activerecord ruby-on-rails

我需要在rails中的列上创建一个不区分大小写的索引.我是通过SQL做到的:

execute(
   "CREATE UNIQUE INDEX index_users_on_lower_email_index 
    ON users (lower(email))"
 )
Run Code Online (Sandbox Code Playgroud)

这很好用,但在我的schema.rb文件中我有:

add_index "users", [nil], 
  :name => "index_users_on_lower_email_index", 
  :unique => true
Run Code Online (Sandbox Code Playgroud)

注意"无".因此,当我尝试克隆数据库以运行测试时,我得到一个明显的错误.我在这里做错了吗?我应该在rails中使用其他一些约定吗?

谢谢您的帮助.

Mar*_*rry 36

由于MySQL索引已经不区分大小写,我猜你正在处理PostgreSQL,默认情况下会创建区分大小写的索引.我在这里基于Rails 3.2.3和PostgreSQL 8.4回答.

似乎功能索引是ActiveRecord无法生成的事物的又一个例子.外键和UUID列是另外两个想到的.所以没有选择(除了猴子修补ActiveRecord),而是使用execute语句.

这意味着要准确转储数据库,您需要放弃与数据库无关的schema.rb,转而使用特定于DB的structure.sql.请参阅"迁移Rails指南",第6.2"架构转储类型".设置如下:

配置/ application.rb中

config.active_record.schema_format = :sql
Run Code Online (Sandbox Code Playgroud)

运行迁移时,应自动更新db/structure.sql.您可以使用以下命令手动生成它:

rake db:structure:dump
Run Code Online (Sandbox Code Playgroud)

该文件是纯Postgres SQL.虽然在rake -T用于列出rake任务时没有记录,但似乎可以使用此命令从structure.sql转储加载数据库:

rake db:structure:load
Run Code Online (Sandbox Code Playgroud)

这里没有什么神奇之处:源代码(在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
Run Code Online (Sandbox Code Playgroud)


pra*_*gma 25

如果您使用的是PostgreSQL,则可以将列类型更改为citext- 不区分大小写的字符串.它还使搜索独立于寄存器.

def change
  enable_extension :citext
  change_column :users, :email, :citext
  add_index :users, :email, unique: true
end
Run Code Online (Sandbox Code Playgroud)

  • @aec应该没有存储或性能上的差异.请参阅:http://www.postgresql.org/docs/9.1/static/datatype-character.html (3认同)
  • 请注意,PostgreSQL 尚未优化 citext 字段的 LIKE 或 ILIKE 条件。请参阅:http://dba.stackexchange.com/questions/105244/index-on-column-with-data-type-citext-not-used (2认同)

Jes*_*ott 7

我会简化这个......

在你的模型中:

before_validation :downcase_email

def downcase_email
  self.email = email.downcase
end
Run Code Online (Sandbox Code Playgroud)

这样,索引与数据库无关,并且您的电子邮件在数据库中都是小写的.

  • 对不起,如果我的措词没有了.我想说是的,如果您在将电子邮件或其他任何您想要索引的内容信息丢弃之前,可以简化索引,然后再将其保存到数据库中.使用电子邮件地址,您可能会使用它 - 电子邮件地址仍然可以使用 - 但有些收件人不喜欢它.总的来说,我认为保留用户提供的信息(包括案例,重点标记等)应该优先于技术简化. (9认同)
  • 可行,但如果您向希望在其电子邮件地址中查看案例的人发送电子邮件(例如BobSmith@example.com),则可能不会受到赞赏. (8认同)
  • @MarkBerry无法判断你是否在拖钓. (6认同)

Mil*_*ric -2

我认为您需要如下所示的列名称

   add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }
Run Code Online (Sandbox Code Playgroud)

而且您必须在数据库中使用适当的不区分大小写的排序规则来创建电子邮件字段,这样您的索引也将不区分大小写。

根据您使用的数据库引擎,语法可能会有所不同,但是

alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...
Run Code Online (Sandbox Code Playgroud)

当您向该列添加索引时,它将不区分大小写。