Jes*_*sen 206 activerecord ruby-on-rails case-insensitive
我的产品型号包含一些商品
Product.first
=> #<Product id: 10, name: "Blue jeans" >
Run Code Online (Sandbox Code Playgroud)
我现在从另一个数据集导入一些产品参数,但名称的拼写有不一致之处.例如,在其他数据集中,Blue jeans可以拼写Blue Jeans.
我想Product.find_or_create_by_name("Blue Jeans"),但这将创造一个新产品,几乎与第一个相同.如果我想找到并比较小写的名字,我有什么选择.
性能问题在这里并不重要:只有100-200个产品,我想将其作为导入数据的迁移来运行.
有任何想法吗?
ale*_*dev 354
你可能必须在这里更加冗长
name = "Blue Jeans"
model = Product.where('lower(name) = ?', name.downcase).first
model ||= Product.create(:name => name)
Run Code Online (Sandbox Code Playgroud)
oma*_*oma 99
这是Rails中的完整设置,供我自己参考.如果它对你有帮助我很高兴.
查询:
Product.where("lower(name) = ?", name.downcase).first
Run Code Online (Sandbox Code Playgroud)
验证者:
validates :name, presence: true, uniqueness: {case_sensitive: false}
Run Code Online (Sandbox Code Playgroud)
索引(从Rails/ActiveRecord中的Case-insensitive唯一索引回答?):
execute "CREATE UNIQUE INDEX index_products_on_lower_name ON products USING btree (lower(name));"
Run Code Online (Sandbox Code Playgroud)
我希望有一个更美好的方式来做第一个和最后一个,但是再一次,Rails和ActiveRecord是开源的,我们不应该抱怨 - 我们可以自己实现它并发送pull请求.
Vie*_*iet 26
如果您使用的是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
Run Code Online (Sandbox Code Playgroud)
要测试它你应该期望以下:
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">
Run Code Online (Sandbox Code Playgroud)
Soh*_*han 21
您可能想要使用以下内容:
validates_uniqueness_of :name, :case_sensitive => false
Run Code Online (Sandbox Code Playgroud)
请注意,默认设置为:case_sensitive => false,因此如果您没有更改其他方式,则甚至不需要编写此选项.
有关更多信息,请访问:http: //api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of
tom*_*nek 13
在postgres:
user = User.find(:first, :conditions => ['username ~* ?', "regedarek"])
Run Code Online (Sandbox Code Playgroud)
Bra*_*rth 10
有几条评论提到了Arel,没有提供一个例子.
以下是不区分大小写搜索的Arel示例:
Product.where(Product.arel_table[:name].matches('Blue Jeans'))
Run Code Online (Sandbox Code Playgroud)
这种解决方案的优点是它与数据库无关 - 它将为您当前的适配器使用正确的SQL命令(matches将ILIKE用于Postgres,以及LIKE其他所有内容).
小智 10
类似于排名第一的安德鲁斯:
对我有用的是:
name = "Blue Jeans"
Product.find_by("lower(name) = ?", name.downcase)
Run Code Online (Sandbox Code Playgroud)
这消除了在同一查询中执行#whereand的需要。#first希望这可以帮助!
引用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
Run Code Online (Sandbox Code Playgroud)
所以你可以这样做:
name = 'Blue jeans'
if prod = Product.find(:conditions => ['name LIKE ?', name])
# update product or whatever
else
prod = Product.create(:name => name)
end
Run Code Online (Sandbox Code Playgroud)
不#find_or_create,我知道,它可能不是非常跨数据库友好,但值得一看?
小智 7
另一种选择可以是
c = Product.find_by("LOWER(name)= ?", name.downcase)
Run Code Online (Sandbox Code Playgroud)
大写和小写字母只有一位不同 - 搜索它们的最有效方法是忽略这一位,而不是转换为低位或高位等.请参阅关键字COLLATION for MS SQL,如果使用Oracle,请参阅NLS_SORT = BINARY_CI,等等..
Find_or_create 现在已弃用,您应该使用 AR Relation 代替加上 first_or_create,如下所示:
TombolaEntry.where("lower(name) = ?", self.name.downcase).first_or_create(name: self.name)
Run Code Online (Sandbox Code Playgroud)
这将返回第一个匹配的对象,或者如果不存在则为您创建一个。