227

私の製品モデルにはいくつかのアイテムが含まれています

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

現在、別のデータセットからいくつかの製品パラメータをインポートしていますが、名前のスペルに矛盾があります。たとえば、他のデータセットでBlue jeansは、スペルを指定できますBlue Jeans

やりたかったProduct.find_or_create_by_name("Blue Jeans")のですが、これで最初の商品とほぼ同じ新商品ができあがります。小文字の名前を見つけて比較したい場合の私の選択肢は何ですか。

ここでは、パフォーマンスの問題はそれほど重要ではありません。製品は100〜200しかなく、データをインポートする移行としてこれを実行したいと思います。

何か案は?

4

19 に答える 19

395

ここではおそらくもっと冗長にする必要があります

name = "Blue Jeans"
model = Product.where('lower(name) = ?', name.downcase).first 
model ||= Product.create(:name => name)
于 2010-02-08T09:35:23.083 に答える
112

これは、私自身の参考のために、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はオープンソースなので、文句を言う必要はありません。自分で実装してプルリクエストを送信できます。

于 2013-03-06T11:07:20.173 に答える
35

PostegresおよびRails4+を使用している場合は、列タイプ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">
于 2016-07-18T18:41:01.807 に答える
22

次のものを使用することをお勧めします。

validates_uniqueness_of :name, :case_sensitive => false

デフォルトでは、設定は:case_sensitive => falseであるため、他の方法を変更していない場合は、このオプションを記述する必要はありません。

詳細については、http: //api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_ofをご覧 ください。

于 2010-02-08T18:11:48.140 に答える
20

いくつかのコメントは、例を提供することなく、Arelに言及しています。

大文字と小文字を区別しない検索のArelの例を次に示します。

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

このタイプのソリューションの利点は、データベースに依存しないことです。現在のアダプターに対して正しいSQLコマンドを使用します(Postgresおよびその他すべてに使用されます)matchesILIKELIKE

于 2017-06-20T22:39:38.387 に答える
14

ポストグレス:

 user = User.find(:first, :conditions => ['username ~* ?', "regedarek"])
于 2012-03-05T21:43:47.690 に答える
9

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、私は知っています、そしてそれはデータベース間であまり友好的ではないかもしれませんが、一見の価値がありますか?

于 2010-02-08T10:04:01.043 に答える
7

誰も言及していない別のアプローチは、大文字と小文字を区別しないファインダーをActiveRecord::Baseに追加することです。詳細はこちらをご覧ください。このアプローチの利点は、すべてのモデルを変更する必要がなくlower()、大文字と小文字を区別しないすべてのクエリに句を追加する必要がなく、代わりに別のファインダーメソッドを使用することです。

于 2010-04-03T19:55:57.837 に答える
6

大文字と小文字の違いは1ビットだけです。それらを検索する最も効率的な方法は、このビットを無視することであり、下位または上位などを変換しないことCOLLATIONです。MSSQLのキーワードを参照NLS_SORT=BINARY_CIし、Oracleを使用しているかどうかなどを確認してください。

于 2010-07-29T18:32:21.070 に答える
6

#1であるアンドリュースに似ています。

私のために働いたものは次のとおりです。

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

これにより、同じクエリで#whereとを実行する必要がなくなります。#firstお役に立てれば!

于 2019-04-08T22:03:14.367 に答える
5

Find_or_createは非推奨になりました。代わりに、ARリレーションとfirst_or_createを次のように使用する必要があります。

TombolaEntry.where("lower(name) = ?", self.name.downcase).first_or_create(name: self.name)

これにより、最初に一致したオブジェクトが返されます。オブジェクトが存在しない場合は、オブジェクトが作成されます。

于 2013-07-02T09:39:48.223 に答える
3

大文字と小文字を区別しない検索には、Railsが組み込まれています。これは、データベースの実装の違いを説明しています。組み込みのArelライブラリ、またはSqueelのようなgemのいずれかを使用します。

于 2013-12-06T20:53:26.063 に答える
2

ここには、特に@omaのすばらしい答えがたくさんあります。ただし、もう1つ試すことができるのは、カスタム列のシリアル化を使用することです。すべてを小文字でデータベースに保存してもかまわない場合は、次を作成できます。

# lib/serializers/downcasing_string_serializer.rb
module Serializers
  class DowncasingStringSerializer
    def self.load(value)
      value
    end

    def self.dump(value)
      value.downcase
    end
  end
end

次に、モデルで:

# app/models/my_model.rb
serialize :name, Serializers::DowncasingStringSerializer
validates_uniqueness_of :name, :case_sensitive => false

このアプローチの利点は、find_or_create_byカスタムスコープ、関数を使用lower(name) = ?したり、クエリを使用したりしなくても、すべての通常のファインダー(を含む)を引き続き使用できることです。

欠点は、データベース内のケーシング情報が失われることです。

于 2015-03-09T17:14:43.133 に答える
2

代替案は

c = Product.find_by("LOWER(name)= ?", name.downcase)
于 2019-04-26T16:15:46.557 に答える
1

以下のようなスコープを使用して、それらを懸念事項に入れ、必要になる可能性のあるモデルに含めることもできます。

scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }

次に、次のように使用します。 Model.ci_find('column', 'value')

于 2016-11-12T00:39:39.817 に答える
0

mysqlを使用すると仮定すると、大文字と小文字を区別しないフィールドを使用できます:http: //dev.mysql.com/doc/refman/5.0/en/case-sensitivevity.html

于 2010-02-08T09:37:55.083 に答える
0
user = Product.where(email: /^#{email}$/i).first
于 2014-09-10T10:30:08.893 に答える
0

LIKEまたはILIKEを使用して表示する人もいますが、それらは正規表現検索を許可します。また、Rubyでダウンケースする必要はありません。データベースに任せることができます。もっと速いかもしれないと思います。後もfirst_or_create使用できますwhere

# app/models/product.rb
class Product < ActiveRecord::Base

  # case insensitive name
  def self.ci_name(text)
    where("lower(name) = lower(?)", text)
  end
end

# first_or_create can be used after a where clause
Product.ci_name("Blue Jeans").first_or_create
# Product Load (1.2ms)  SELECT  "products".* FROM "products"  WHERE (lower(name) = lower('Blue Jeans'))  ORDER BY "products"."id" ASC LIMIT 1
# => #<Product id: 1, name: "Blue jeans", created_at: "2016-03-27 01:41:45", updated_at: "2016-03-27 01:41:45"> 
于 2016-03-27T01:36:33.420 に答える
-10

これまで、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

これにより、名前が一致する最初の製品が得られます。またはnil。

>> 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 :)
于 2010-02-08T09:38:36.627 に答える