จะทำแบบสอบถาม LIKE ใน Arel และ Rails ได้อย่างไร?


115

ฉันต้องการทำสิ่งที่ชอบ:

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

ความพยายามของฉันใน Arel:

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

อย่างไรก็ตามสิ่งนี้จะกลายเป็น:

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

Arel ตัดสตริงข้อความค้นหา "Smith" อย่างถูกต้อง แต่เนื่องจากนี่เป็นคำสั่ง LIKE จึงไม่สามารถใช้งานได้

คำถาม LIKE ใน Arel ทำอย่างไร

PS Bonus - จริงๆแล้วฉันกำลังพยายามสแกนสองฟิลด์บนตารางทั้งชื่อและคำอธิบายเพื่อดูว่าตรงกับข้อความค้นหาหรือไม่ วิธีการที่จะทำงาน?


1
ฉันอัปเดตคำตอบ arel สำหรับโบนัส
Pedro Rolo

คำตอบ:


275

นี่คือวิธีที่คุณดำเนินการค้นหา like ใน 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
ซึ่งแตกต่างจากการใช้where("name like ?", ...)วิธีนี้สามารถพกพาได้มากกว่าในฐานข้อมูลต่างๆ ตัวอย่างเช่นจะส่งผลให้ILIKEมีการใช้แบบสอบถามกับ Postgres db
dkobozev

20
สิ่งนี้ได้รับการป้องกันจากการแทรก SQL หรือไม่
sren

7
สิ่งนี้ไม่ได้ป้องกันการแทรก SQL อย่างเต็มที่ ลองตั้งค่า user_name เป็น "%" คำค้นหาจะส่งคืนการแข่งขัน
travis-146

5
ฉันพยายามฉีด sql โดยใช้ params โดยตรงUser.where(users[:name].matches("%#{params[:user_name]}%"))ฉันลองTRUNCATE users;และแบบสอบถามอื่น ๆ แล้วและไม่มีอะไรเกิดขึ้นที่ด้าน sql ดูปลอดภัยสำหรับฉัน
earlonrails

5
ใช้.gsub(/[%_]/, '\\\\\0')สำหรับการหลบหนีอักขระตัวแทน MySql
aercolino

116

ลอง

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

PS

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

มันเป็นเวลานานแล้ว แต่ @ cgg5207 ได้เพิ่มการปรับเปลี่ยน (ส่วนใหญ่มีประโยชน์หากคุณจะค้นหาพารามิเตอร์ที่มีชื่อยาวหรือชื่อยาวหลายตัวหรือคุณขี้เกียจพิมพ์)

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

หรือ

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

9
Rails รู้ได้อย่างไรว่าจะไม่หลบหนี%ในสตริงที่ถูกแทนที่ ดูเหมือนว่าหากคุณต้องการเพียงตัวแทนด้านเดียวไม่มีอะไรหยุดผู้ใช้จากการส่งค่าการสืบค้นที่มี%ทั้งสองด้าน (ฉันรู้ว่าในทางปฏิบัติ Rails ป้องกันไม่ให้%แสดงในสตริงข้อความค้นหา แต่ดูเหมือนว่าจะมี ควรป้องกันสิ่งนี้ในระดับ ActiveRecord)
Steven

8
สิ่งนี้ไม่เสี่ยงต่อการโจมตีด้วยการฉีด SQL?
Behrang Saeedzadeh

7
@Behrang no 8) User.where ("ชื่อเหมือน% # {params [: query]}% หรือคำอธิบายเช่น% # {params [: query]}%") to_sql จะมีความเสี่ยง แต่ในรูปแบบที่ฉันแสดง , Rails Escape params [: query]
Reuben Mallaby

ขออภัยสำหรับ offtopic ฉันมี sql git ของ method to_sqlหรือ arel manager จะรัน sql บน db ได้อย่างไร
МалъСкрылевъ

Model.where (to_sql_result)
Pedro Rolo

4

คำตอบของ Reuben Mallaby สามารถย่อให้สั้นลงเพื่อใช้การผูกพารามิเตอร์:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.