私の製品モデルにはいくつかのアイテムが含まれています
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および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であるため、他の方法を変更していない場合、このオプションを記述する必要はありません。
Postgresの場合:
user = User.find(:first, :conditions => ['username ~* ?', "regedarek"])
いくつかのコメントは、例を提供せずにArelを参照しています。
大文字と小文字を区別しない検索のArelの例を次に示します。
Product.where(Product.arel_table[:name].matches('Blue Jeans'))
このタイプのソリューションの利点は、データベースに依存しないことです。現在のアダプターに正しいSQLコマンドを使用します(matches
はPostgresにILIKE
を使用し、すべてに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
ではない、私は知っているし、データベース間であまり友好的ではないかもしれないが、見る価値はあるのか?
大文字と小文字は1ビットだけ異なります。それらを検索する最も効率的な方法は、下位または上位などを変換せずに、このビットを無視することです。MSSQLのキーワードCOLLATION
、Oracleを使用する場合はNLS_SORT=BINARY_CI
を参照してください。
誰も言及していない別のアプローチは、大文字と小文字を区別しないファインダーをActiveRecord :: Baseに追加することです。詳細は こちら をご覧ください。このアプローチの利点は、すべてのモデルを変更する必要がなく、すべての大文字と小文字を区別しないクエリにlower()
句を追加する必要がないことです。代わりに異なるFinderメソッドを使用します。
Find_or_createは非推奨になりました。代わりに、ARリレーションとfirst_or_createを使用する必要があります。
TombolaEntry.where("lower(name) = ?", self.name.downcase).first_or_create(name: self.name)
これは、最初に一致したオブジェクトを返すか、存在しない場合は作成します。
ここには、特に@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
このアプローチの利点は、カスタムスコープ、関数を使用したり、クエリでlower(name) = ?
を使用したりせずに、すべての通常のファインダー(find_or_create_by
を含む)を使用できることです。
欠点は、データベース内のケーシング情報が失われることです。
大文字と小文字を区別しない検索がRailsに組み込まれています。データベース実装の違いを説明します。 組み込みのArelライブラリ、またはSqueelのようなgem を使用します。
また、以下のようなスコープを使用して懸念に入れ、必要なモデルに含めることもできます。
scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }
次に、次のように使用します:Model.ci_find('column', 'value')
user = Product.where(email: /^#{email}$/i).first
Mysqlを使用すると仮定すると、大文字と小文字を区別しないフィールドを使用できます。 http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
#1のアンドリュースに似ています:
私のために働いたものは次のとおりです:
name = "Blue Jeans"
Product.find_by("lower(name) = ?", name.downcase)
これにより、同じクエリで#where
と#first
を実行する必要がなくなります。お役に立てれば!
代替手段は
c = Product.find_by("LOWER(name)= ?", name.downcase)
一部の人々は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">