Моя модель продукта содержит некоторые объекты
Product.first
=> #<Product id: 10, name: "Blue jeans" >
Я теперь импортирую некоторые параметры продукта из другого набора данных, но существуют несоответствия в написании имен. Например, в другом наборе данных, Blue jeans
мог быть записан Blue Jeans
.
Я хотел Product.find_or_create_by_name("Blue Jeans")
, но это создаст новый продукт, почти идентичный первому. Каковы мои опции, если я хочу найти и сравнить печатавшее строчными литерами имя.
Проблемы производительности не действительно важны здесь: существует только 100-200 продуктов, и я хочу выполнить это как миграцию, которая импортирует данные.
Какие-либо идеи?
Вам, вероятно, придется быть более подробным здесь
name = "Blue Jeans"
model = Product.where('lower(name) = ?', name.downcase).first
model ||= Product.create(:name => name)
Цитирую по документации 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
, я знаю, и это может быть не очень дружелюбно к базе данных, но стоит посмотреть?
Вы можете использовать следующее:
validates_uniqueness_of :name, :case_sensitive => false
Обратите внимание, что по умолчанию настройка: case_sensitive => false, поэтому вам даже не нужно написать этот вариант, если вы не изменили другие способы.
Дополнительные сведения см. По адресу: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of
До сих пор я делал решение, используя Ruby. Поместите это в модель продукта:
#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 :)
Предполагая, что вы используете mysql, вы можете использовать поля без учета регистра: http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
Другой подход, о котором никто не упоминал, - это добавление средств поиска без учета регистра в ActiveRecord :: Base. Подробности можно найти здесь . Преимущество этого подхода заключается в том, что вам не нужно изменять каждую модель, и вам не нужно добавлять предложение lower ()
во все ваши нечувствительные к регистру запросы, вы просто используете другой метод поиска вместо.
Буквы верхнего и нижнего регистра отличаются только одним битом. Самый эффективный способ их поиска - игнорировать этот бит, не преобразовывать нижнее или верхнее и т. Д. См. Ключевые слова COLLATION
для MSSQL, см. NLS_SORT = BINARY_CI
при использовании Oracle и т. Д.