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

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

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

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

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

什么好主意吗?


当前回答

在postgres:

 user = User.find(:first, :conditions => ['username ~* ?', "regedarek"])

其他回答

如果您正在使用Postegres和Rails 4+,那么您可以选择使用列类型CITEXT,这将允许不区分大小写的查询,而不必写出查询逻辑。

迁移:

def change
  enable_extension :citext
  change_column :products, :name, :citext
  add_index :products, :name, unique: true # If you want to index the product names
end

为了测试它,你应该期待以下内容:

Product.create! name: 'jOgGers'
=> #<Product id: 1, name: "jOgGers">

Product.find_by(name: 'joggers')
=> #<Product id: 1, name: "jOgGers">

Product.find_by(name: 'JOGGERS')
=> #<Product id: 1, name: "jOgGers">

另一种没有人提到的方法是在ActiveRecord::Base中添加不区分大小写的查找器。详情可以在这里找到。这种方法的优点是不必修改每个模型,也不必向所有不区分大小写的查询添加lower()子句,只需使用不同的查找器方法即可。

到目前为止,我使用Ruby编写了一个解决方案。把它放在Product模型中:

  #return first of matching products (id only to minimize memory consumption)
  def self.custom_find_by_name(product_name)
    @@product_names ||= Product.all(:select=>'id, name')
    @@product_names.select{|p| p.name.downcase == product_name.downcase}.first
  end

  #remember a way to flush finder cache in case you run this from console
  def self.flush_custom_finder_cache!
    @@product_names = nil
  end

这将给我第一个名称匹配的产品。或零。

>> Product.create(:name => "Blue jeans")
=> #<Product id: 303, name: "Blue jeans">

>> Product.custom_find_by_name("Blue Jeans")
=> nil

>> Product.flush_custom_finder_cache!
=> nil

>> Product.custom_find_by_name("Blue Jeans")
=> #<Product id: 303, name: "Blue jeans">
>>
>> #SUCCESS! I found you :)

类似于安德鲁斯的第一条:

对我有用的是:

name = "Blue Jeans"
Product.find_by("lower(name) = ?", name.downcase)

这样就不需要在同一个查询中执行#where和#first。希望这能有所帮助!

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