我的产品型号包含一些项目
Product.first
=> #<Product id: 10, name: "Blue jeans" >
我现在从另一个数据集导入一些产品参数,但是名称的拼写不一致。例如,在另一个数据集中,Blue jeans可以拼写为Blue jeans。
我想要产品。find_or_create_by_name("Blue Jeans"),但这将创建一个与第一个几乎相同的新产品。我的选择是什么,如果我想找到和比较小写的名字。
性能问题在这里并不重要:只有100-200个产品,我希望将其作为导入数据的迁移来运行。
什么好主意吗?
如果您正在使用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">
引用SQLite文档:
任何其他字符匹配自身或
它的小写/大写等价(即。
不区分大小写匹配)
...我不知道。但它是有效的:
sqlite> create table products (name string);
sqlite> insert into products values ("Blue jeans");
sqlite> select * from products where name = 'Blue Jeans';
sqlite> select * from products where name like 'Blue Jeans';
Blue jeans
所以你可以这样做:
name = 'Blue jeans'
if prod = Product.find(:conditions => ['name LIKE ?', name])
# update product or whatever
else
prod = Product.create(:name => name)
end
不是#find_or_create,我知道,它可能不是很跨数据库友好,但值得一看?
到目前为止,我使用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 :)
这是Rails中的一个完整设置,供我自己参考。如果这对你也有帮助,我很高兴。
查询:
Product.where("lower(name) = ?", name.downcase).first
验证器:
validates :name, presence: true, uniqueness: {case_sensitive: false}
索引(答案来自Rails/ActiveRecord中不区分大小写的唯一索引?):
execute "CREATE UNIQUE INDEX index_products_on_lower_name ON products USING btree (lower(name));"
我希望有一种更漂亮的方式来完成第一个和最后一个,但话又说回来,Rails和ActiveRecord是开源的,我们不应该抱怨——我们可以自己实现它并发送pull request。