[ruby-on-rails] Rails 모델에서 대소 문자를 구분하지 않는 검색

내 제품 모델에 일부 항목이 포함되어 있습니다

 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)


답변

이것은 내가 참조 할 수 있도록 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는 오픈 소스입니다. 우리는 불평해서는 안됩니다. 우리는 직접 구현하고 풀 요청을 보낼 수 있습니다.


답변

Postegres and 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">


답변

다음을 사용할 수 있습니다.

validates_uniqueness_of :name, :case_sensitive => false

기본적으로 설정은 : case_sensitive => false이므로 다른 방법으로 변경하지 않은 경우이 옵션을 작성할 필요도 없습니다.

http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of 에서 자세한 내용을 확인하십시오.


답변

postgres에서 :

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


답변

몇 가지 의견은 예제를 제공하지 않고 Arel을 나타냅니다.

대소 문자를 구분하지 않는 검색의 Arel 예는 다음과 같습니다.

Product.where(Product.arel_table[:name].matches('Blue Jeans'))

이 유형의 솔루션의 장점은 데이터베이스에 구애받지 않는다는 것입니다. 현재 어댑터에 올바른 SQL 명령을 matches사용 ILIKE합니다 (Postgres 및 LIKE기타 모든 것에 사용).


답변

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, 나는 알고 있으며 데이터베이스 간 매우 친숙하지는 않지만 볼 가치가 있습니까?