Làm thế nào để thực hiện một truy vấn LIKE trong Arel và Rails?


115

Tôi muốn làm một cái gì đó như:

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

Nỗ lực của tôi ở Arel:

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

Tuy nhiên, điều này trở thành:

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

Arel kết thúc chuỗi truy vấn 'Smith' một cách chính xác, nhưng vì đây là câu lệnh LIKE nên nó không hoạt động.

Làm cách nào để thực hiện một truy vấn LIKE trong Arel?

PS Bonus - Tôi thực sự đang cố gắng quét hai trường trên bảng, cả tên và mô tả, để xem có bất kỳ trường nào phù hợp với truy vấn không. Nó sẽ hoạt động như thế nào?


1
Tôi đã cập nhật câu trả lời isl cho phần thưởng.
Pedro Rolo

Câu trả lời:


275

Đây là cách bạn thực hiện một truy vấn like trong arel:

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

Tái bút:

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
Không giống như cách sử dụng where("name like ?", ...), cách tiếp cận này linh hoạt hơn trên các cơ sở dữ liệu khác nhau. Ví dụ, nó sẽ dẫn đến ILIKEviệc được sử dụng trong một truy vấn đối với db Postgres.
dkobozev

20
điều này có được bảo vệ chống lại việc tiêm SQL không?
sren

7
Điều này KHÔNG bảo vệ hoàn toàn khỏi việc tiêm SQL. Thử đặt user_name thành "%". Truy vấn sẽ trở lại trận đấu
Travis-146

5
Tôi đã cố gắng tiêm sql bằng cách sử dụng trực tiếp params User.where(users[:name].matches("%#{params[:user_name]}%")), tôi đã thử TRUNCATE users;và các truy vấn khác như vậy và không có gì xảy ra ở phía sql. Có vẻ an toàn với tôi.
earlonrails

5
Sử dụng .gsub(/[%_]/, '\\\\\0')để thoát các ký tự đại diện MySql.
aercolino

116

Thử

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

Tái bút.

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

Aa và đã lâu rồi nhưng @ cgg5207 đã thêm một sửa đổi (chủ yếu hữu ích nếu bạn đang tìm kiếm các thông số có tên dài hoặc nhiều tên dài hoặc bạn quá lười nhập)

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

hoặc là

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

9
Làm cách nào để Rails biết không thoát %trong chuỗi được thay thế? Có vẻ như nếu bạn chỉ muốn một ký tự đại diện một mặt, không có gì ngăn cản người dùng gửi giá trị truy vấn bao gồm %cả hai đầu (tôi biết rằng trên thực tế, Rails ngăn không %cho hiển thị trong một chuỗi truy vấn, nhưng có vẻ như ở đó nên được bảo vệ chống lại điều này ở cấp ActiveRecord).
Steven

8
Điều này không dễ bị tấn công SQL injection?
Behrang Saeedzadeh

7
@Behrang no 8) User.where ("tên như% # {params [: query]}% hoặc mô tả như% # {params [: query]}%"). To_sql sẽ dễ bị tấn công, nhưng ở định dạng tôi hiển thị , Rails thoát khỏi params [: query]
Reuben Mallaby

Xin lỗi cho offtopic. Tôi có git sql của phương thức to_sqlhoặc trình quản lý arel, làm thế nào để thực thi sql trên db?
Малъ Скрылевъ

Model.where (to_sql_result)
Pedro Rolo,

4

Câu trả lời của Reuben Mallaby có thể được rút ngắn hơn nữa để sử dụng các ràng buộc tham số:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.