Model produk saya mengandung beberapa item
Product.first
=> #<Product id: 10, name: "Blue jeans" >
Saya sekarang mengimpor beberapa parameter produk dari dataset lain, tetapi ada ketidakkonsistenan dalam pengejaan nama. Misalnya, dalam dataset lain, Blue jeans
bisa dieja Blue Jeans
.
Saya ingin Product.find_or_create_by_name("Blue Jeans")
, tetapi ini akan membuat produk baru, hampir identik dengan yang pertama. Apa pilihan saya jika saya ingin mencari dan membandingkan nama yang lebih kecil.
Masalah kinerja tidak terlalu penting di sini: Hanya ada 100-200 produk, dan saya ingin menjalankan ini sebagai migrasi yang mengimpor data.
Ada ide?
ruby-on-rails
activerecord
case-insensitive
Jesper Rønn-Jensen
sumber
sumber
"$##"
dan'$##'
. Yang pertama adalah interpolasi (tanda kutip ganda). Yang kedua bukan. Input pengguna tidak pernah diinterpolasi.find(:first)
sudah usang, dan opsi sekarang adalah menggunakan#first
. Dengan demikian,Product.first(conditions: [ "lower(name) = ?", name.downcase ])
model = Product.where('lower(name) = ?', name.downcase).first_or_create
after_create
callback dalamProduct
model dan di dalam callback, kita memilikiwhere
klausa, misalnyaproducts = Product.where(country: 'us')
. Dalam hal ini,where
klausa dirantai ketika callback dieksekusi dalam konteks lingkup. Hanya FYI.Ini adalah pengaturan lengkap dalam Rails, untuk referensi saya sendiri. Saya senang jika itu membantu Anda juga.
pertanyaan:
validator:
indeks (jawaban dari indeks unik Case-insensitive di Rails / ActiveRecord? ):
Saya berharap ada cara yang lebih indah untuk melakukan yang pertama dan yang terakhir, tetapi sekali lagi, Rails dan ActiveRecord adalah open source, kita tidak boleh mengeluh - kita bisa menerapkannya sendiri dan mengirim permintaan tarik.
sumber
find(:first, ...)
sekarang sudah usang, saya pikir ini adalah jawaban yang paling tepat.Product.where("lower(name) = ?", name).first
Jika Anda menggunakan Postegres dan Rails 4+, maka Anda memiliki opsi untuk menggunakan tipe kolom CITEXT, yang akan memungkinkan kueri case-case tidak sensitif tanpa harus menuliskan logika kueri.
Migrasi:
Dan untuk mengujinya Anda harus mengharapkan yang berikut:
sumber
Anda mungkin ingin menggunakan yang berikut ini:
Harap dicatat bahwa secara default pengaturannya adalah: case_sensitive => false, jadi Anda bahkan tidak perlu menulis opsi ini jika Anda belum mengubah cara lain.
Temukan lebih lanjut di: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of
sumber
Dalam postgres:
sumber
Beberapa komentar merujuk pada Arel, tanpa memberikan contoh.
Berikut ini adalah contoh Arel dari pencarian case-insensitive:
Keuntungan dari solusi ini adalah database-agnostik - ia akan menggunakan perintah SQL yang benar untuk adaptor Anda saat ini (
matches
akan digunakanILIKE
untuk Postgres, danLIKE
untuk yang lainnya).sumber
Mengutip dari dokumentasi SQLite :
... yang saya tidak tahu. Tapi itu bekerja:
Jadi Anda bisa melakukan sesuatu seperti ini:
Tidak
#find_or_create
, saya tahu, dan itu mungkin tidak ramah lintas-basis data, tetapi layak untuk dilihat?sumber
Pendekatan lain yang belum ada yang disebutkan adalah menambahkan case finders tidak sensitif ke dalam ActiveRecord :: Base. Detail dapat ditemukan di sini . Keuntungan dari pendekatan ini adalah Anda tidak perlu memodifikasi setiap model, dan Anda tidak harus menambahkan
lower()
klausa ke semua pertanyaan case sensitif Anda, Anda hanya menggunakan metode finder yang berbeda sebagai gantinya.sumber
Huruf besar dan kecil hanya berbeda satu bit. Cara paling efisien untuk mencari mereka adalah dengan mengabaikan bit ini, bukan untuk mengkonversi lebih rendah atau lebih tinggi, dll. Lihat kata kunci
COLLATION
untuk MSSQL, lihatNLS_SORT=BINARY_CI
apakah menggunakan Oracle, dll.sumber
Find_or_create sekarang sudah usang, Anda harus menggunakan Relasi AR sebagai gantinya ditambah first_or_create, seperti:
Ini akan mengembalikan objek yang cocok pertama, atau membuatnya untuk Anda jika tidak ada.
sumber
Pencarian case-insensitive terintegrasi dengan Rails. Ini menjelaskan perbedaan dalam implementasi database. Gunakan pustaka Arel bawaan, atau permata seperti Squeel .
sumber
Ada banyak jawaban bagus di sini, terutama @ oma. Tetapi satu hal lain yang bisa Anda coba adalah menggunakan serialisasi kolom khusus. Jika Anda tidak keberatan semuanya disimpan huruf kecil di db Anda maka Anda dapat membuat:
Kemudian dalam model Anda:
Manfaat dari pendekatan ini adalah Anda masih dapat menggunakan semua pencari reguler (termasuk
find_or_create_by
) tanpa menggunakan cakupan kustom, fungsi, atau memilikilower(name) = ?
dalam permintaan Anda.Kelemahannya adalah Anda kehilangan informasi casing dalam database.
sumber
Mirip dengan Andrews yang merupakan # 1:
Sesuatu yang bekerja untuk saya adalah:
Ini menghilangkan kebutuhan untuk melakukan
#where
dan#first
dalam permintaan yang sama. Semoga ini membantu!sumber
Anda juga dapat menggunakan lingkup seperti ini di bawah ini dan menempatkannya dalam kekhawatiran dan memasukkan dalam model yang Anda mungkin membutuhkannya:
scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }
Kemudian gunakan seperti ini:
Model.ci_find('column', 'value')
sumber
Dengan asumsi bahwa Anda menggunakan mysql, Anda bisa menggunakan bidang yang tidak peka huruf besar-kecil: http://dev.mysql.com/doc/refman/5.0/id/case-sensitivity.html
sumber
sumber
TypeError: Cannot visit Regexp
Beberapa orang menunjukkan menggunakan LIKE atau ILIKE, tetapi itu memungkinkan pencarian regex. Anda juga tidak perlu downcase di Ruby. Anda dapat membiarkan database melakukannya untuk Anda. Saya pikir mungkin lebih cepat. Juga
first_or_create
bisa digunakan setelahnyawhere
.sumber
Sebuah alternatif bisa jadi
sumber
Sejauh ini, saya membuat solusi menggunakan Ruby. Tempatkan ini di dalam model Produk:
Ini akan memberi saya produk pertama yang cocok dengan nama. Atau nihil.
sumber