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?

filsa
sumber
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)))
Pedro Rolo
sumber
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
Ruben Mallaby
sumber
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
cgg5207
sumber