我的产品型号包含一些项目

 Product.first
 => #<Product id: 10, name: "Blue jeans" >

我现在从另一个数据集导入一些产品参数,但是名称的拼写不一致。例如,在另一个数据集中,Blue jeans可以拼写为Blue jeans。

我想要产品。find_or_create_by_name("Blue Jeans"),但这将创建一个与第一个几乎相同的新产品。我的选择是什么,如果我想找到和比较小写的名字。

性能问题在这里并不重要:只有100-200个产品,我希望将其作为导入数据的迁移来运行。

什么好主意吗?


当前回答

你可能想要使用以下语句:

validates_uniqueness_of :name, :case_sensitive => false

请注意,默认设置是:case_sensitive => false,所以如果你没有改变其他方式,你甚至不需要写这个选项。

欲知详情,请浏览: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of

其他回答

大写字母和小写字母只差一位。最有效的搜索方法是忽略这个位,而不是转换上下位等等。如果使用Oracle,请参见NLS_SORT=BINARY_CI等。

假设您使用mysql,您可以使用不区分大小写的字段:http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html

如果您正在使用postgres(可能是其他的),我喜欢这个解决方案。

Product.find_by("name ilike 'bLue JEaNS'")

我更喜欢这个,有几个原因。

更清晰的连接到数据库操作->你可以复制粘贴到哪里… 如果您选择添加一个外接符%,这很简单。

有些人使用LIKE或ILIKE显示,但这些允许正则表达式搜索。在Ruby中你也不需要小写。您可以让数据库为您做这件事。我想可能会快一些。first_or_create也可以用在where后面。

# app/models/product.rb
class Product < ActiveRecord::Base

  # case insensitive name
  def self.ci_name(text)
    where("lower(name) = lower(?)", text)
  end
end

# first_or_create can be used after a where clause
Product.ci_name("Blue Jeans").first_or_create
# Product Load (1.2ms)  SELECT  "products".* FROM "products"  WHERE (lower(name) = lower('Blue Jeans'))  ORDER BY "products"."id" ASC LIMIT 1
# => #<Product id: 1, name: "Blue jeans", created_at: "2016-03-27 01:41:45", updated_at: "2016-03-27 01:41:45"> 

这里有很多很棒的答案,尤其是@oma的。但是您还可以尝试使用自定义列序列化。如果你不介意在你的db中存储小写字母,那么你可以创建:

# lib/serializers/downcasing_string_serializer.rb
module Serializers
  class DowncasingStringSerializer
    def self.load(value)
      value
    end

    def self.dump(value)
      value.downcase
    end
  end
end

然后在你的模型中:

# app/models/my_model.rb
serialize :name, Serializers::DowncasingStringSerializer
validates_uniqueness_of :name, :case_sensitive => false

这种方法的好处是,您仍然可以使用所有常规查找器(包括find_or_create_by),而无需使用自定义作用域、函数或使用较低的(name) = ?在你的查询中。

缺点是会丢失数据库中的套管信息。