Bagaimana cara melakukan kueri LIKE di Arel and Rails?


115

Saya ingin melakukan sesuatu seperti:

SELECT * FROM USER WHERE NAME LIKE '%Smith%';

Upaya saya di Arel:

# params[:query] = 'Smith'
User.where("name like '%?%'", params[:query]).to_sql

Namun, ini menjadi:

SELECT * FROM USER WHERE NAME LIKE '%'Smith'%';

Arel membungkus string kueri 'Smith' dengan benar, tetapi karena ini adalah pernyataan LIKE, ini tidak berfungsi.

Bagaimana cara melakukan query LIKE di Arel?

Bonus PS - Saya sebenarnya mencoba memindai dua bidang pada tabel, nama dan deskripsi, untuk melihat apakah ada kecocokan dengan kueri. Bagaimana cara kerjanya?


1
Saya memperbarui jawaban arel untuk bonus.
— Pedro Rolo

Jawaban:


275

Beginilah cara Anda melakukan kueri serupa di arel:

users = User.arel_table
User.where(users[:name].matches("%#{user_name}%"))

PS:

users = User.arel_table
query_string = "%#{params[query]}%"
param_matches_string =  ->(param){ 
  users[param].matches(query_string) 
} 
User.where(param_matches_string.(:name)\
                       .or(param_matches_string.(:description)))

10
Tidak seperti penggunaan where("name like ?", ...), pendekatan ini lebih portabel di berbagai database. Misalnya, ini akan mengakibatkan ILIKEdigunakan dalam kueri terhadap Postgres db.
— dkobozev

20
apakah ini dilindungi dari injeksi SQL?
— sren

7
Ini TIDAK melindungi sepenuhnya terhadap injeksi SQL. Coba tetapkan user_name ke "%". Kueri akan mengembalikan pertandingan
— travis-146

5
Saya mencoba menyuntikkan sql menggunakan params secara langsung User.where(users[:name].matches("%#{params[:user_name]}%")),, saya mencoba TRUNCATE users;dan pertanyaan lain seperti itu dan tidak ada yang terjadi di sisi sql. Tampak aman bagi saya.
— earlonrails

5
Gunakan .gsub(/[%_]/, '\\\\\0')untuk keluar dari karakter wildcard MySql.
— aercolino

116

Mencoba

User.where("name like ?", "%#{params[:query]}%").to_sql

PS.

q = "%#{params[:query]}%"
User.where("name like ? or description like ?", q, q).to_sql

Aaand sudah lama tetapi @ cgg5207 menambahkan modifikasi (sangat berguna jika Anda akan mencari parameter dengan nama lama atau beberapa nama lama atau Anda terlalu malas untuk mengetik)

q = "%#{params[:query]}%"
User.where("name like :q or description like :q", :q => q).to_sql

atau

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql

9
Bagaimana Rails tahu untuk tidak melarikan diri %dalam string yang diganti? Sepertinya jika Anda hanya menginginkan wildcard satu sisi, tidak ada yang menghentikan pengguna untuk mengirimkan nilai kueri yang mencakup %di kedua ujungnya (saya tahu bahwa dalam praktiknya, Rails mencegah %agar tidak muncul dalam string kueri, tetapi sepertinya di sana harus menjadi perlindungan terhadap ini di level ActiveRecord).
— Steven

8
Bukankah ini rentan terhadap serangan injeksi SQL?
— Behrang Saeedzadeh

7
@Behrang no 8) User.where ("nama seperti% # {params [: query]}% atau deskripsi seperti% # {params [: query]}%"). To_sql akan rentan, tetapi, dalam format yang saya tunjukkan , Rails lolos dari params [: query]
— Reuben Mallaby

Maaf untuk offtopic. Saya memiliki sql git of method to_sqlatau arel manager, bagaimana cara menjalankan sql pada db?
— Малъ Скрылевъ

Model.where (to_sql_result)
— Pedro Rolo

4

Jawaban Reuben Mallaby dapat dipersingkat lebih lanjut untuk menggunakan pengikatan parameter:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.