Rails 4 LIKE query - ActiveRecord เพิ่มเครื่องหมายคำพูด


128

ฉันกำลังพยายามทำแบบสอบถามแบบนี้

def self.search(search, page = 1 )
  paginate :per_page => 5, :page => page,
    :conditions => ["name LIKE '%?%' OR postal_code like '%?%'", search, search],   order => 'name'
end

แต่เมื่อมีการเรียกใช้บางสิ่งบางอย่างกำลังเพิ่มเครื่องหมายคำพูดซึ่งทำให้คำสั่ง sql ออกมาเป็นเช่นนั้น

SELECT COUNT(*)
FROM "schools" 
WHERE (name LIKE '%'havard'%' OR postal_code like '%'havard'%')):

คุณจะเห็นปัญหาของฉัน ฉันใช้ Rails 4 และ Postgres 9 ทั้งคู่ซึ่งฉันไม่เคยใช้เลยไม่แน่ใจว่ามันและแอคทีฟคอร์ดหรืออาจจะเป็นของ postgres

ฉันจะตั้งค่านี้ได้อย่างไรดังนั้นฉันจึงมีเหมือน'%my_search%'ในแบบสอบถามสุดท้าย

คำตอบ:


230

ตัวยึดตำแหน่งของคุณถูกแทนที่ด้วยสตริงและคุณจัดการไม่ถูกต้อง

แทนที่

"name LIKE '%?%' OR postal_code LIKE '%?%'", search, search

กับ

"name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"

6
สิ่งนี้ไม่เสี่ยงต่อการฉีด SQL? ฉันหมายความว่าsearchสตริงเหล่านั้นถูกทำให้สะอาดหรือไม่?
jdscosta91

6
@ jdscosta91 ?ในสถานที่ที่จะดูแลการฆ่าเชื้อ
บ้าน 9

7
อินสแตนซ์ @ house9 ของ%และ_ภายในsearchจะไม่ถูกฆ่าเชื้อภายใต้วิธีนี้
Barry Kelly

8
เมื่อใช้ "?" ด้วยวิธีนี้ในรางจะถูกแปลงเป็นแบบสอบถามแบบกำหนดพารามิเตอร์ ข้อมูลภายในพารามิเตอร์ไม่ได้รับการทำความสะอาด (%) แต่เป็นไปไม่ได้ที่จะเปลี่ยนบริบทออกจากแบบสอบถามและเปลี่ยนเป็นคำสั่ง SQL ที่ประมวลผลแล้ว
David Hoelzer

51

แทนที่จะใช้conditionsไวยากรณ์จาก Rails 2 ให้ใช้whereวิธีของ Rails 4 แทน:

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE :search OR postal_code LIKE :search", search: wildcard_search)
    .page(page)
    .per_page(5)
end

หมายเหตุ: ข้างต้นใช้ไวยากรณ์พารามิเตอร์แทน? ตัวยึด: ทั้งคู่ควรสร้าง sql เดียวกัน

def self.search(search, page = 1 )
  wildcard_search = "%#{search}%"

  where("name ILIKE ? OR postal_code LIKE ?", wildcard_search, wildcard_search)
    .page(page)
    .per_page(5)
end

หมายเหตุ: ใช้ILIKEสำหรับชื่อ - postgres รุ่นที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ของ LIKE


ยังใช้ได้กับ Rails 5 หรือไม่ เพราะถ้าฉันMovie.where("title ILIKE :s", s: search_string)แปลSELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1โดย ActiveRecord (Rails 5.1.6) - โปรดสังเกตว่าไม่มีสัญลักษณ์เปอร์เซ็นต์หลัง ILIKE)
sekmo

@sekmo - ควรใช้งานได้เปอร์เซ็นต์จะอยู่ในsearch_stringตัวแปร ฉันคิดว่าSELECT 1 AS oneเอาต์พุตอยู่ในคอนโซลรางเท่านั้นหรือคุณกำลังใช้limit(1)? FYI: Rails 5.1.6 มีปัญหาด้านความปลอดภัยให้ใช้ 5.1.6.2 หรือ 5.1.7 แทน
house9

23

แม้ว่าการแก้ไขสตริงจะใช้งานได้เนื่องจากคำถามของคุณระบุราง 4 คุณสามารถใช้ Arel สำหรับสิ่งนี้และทำให้ฐานข้อมูลแอปของคุณไม่เชื่อเรื่องพระเจ้า

def self.search(query, page=1)
  query = "%#{query}%"
  name_match = arel_table[:name].matches(query)
  postal_match = arel_table[:postal_code].matches(query)
  where(name_match.or(postal_match)).page(page).per_page(5)
end

ควรจะง่ายในการสร้าง func เพื่อทำสิ่งนี้แบบไดนามิกด้วยรายการคุณลักษณะ
cevaris

ยังไม่ทดสอบแม้ว่าอาจเป็นสิ่งนี้: ขอบเขต search_attributes, -> (แบบสอบถาม, แอตทริบิวต์) {arel_attributes = attributes.map {| a | arel_table [a]} arel_queries = arel_attributes.map {| a | a.matches (query)} คืนค่าโดยที่ (arel_queries.reduce {| res, q | res.or q})}
Pedro Rolo

9

ActiveRecord ฉลาดพอที่จะรู้ว่าพารามิเตอร์ที่อ้างถึง?เป็นสตริงดังนั้นจึงใส่ไว้ในเครื่องหมายคำพูดเดี่ยว คุณสามารถทำได้ตามที่หนึ่งโพสต์แนะนำให้ใช้การแก้ไขสตริง Ruby เพื่อวางสตริงด้วย%สัญลักษณ์ที่ต้องการ อย่างไรก็ตามสิ่งนี้อาจทำให้คุณได้รับการฉีด SQL (ซึ่งไม่ดี) ฉันขอแนะนำให้คุณใช้CONCAT()ฟังก์ชันSQL เพื่อเตรียมสตริงดังนี้:

"name LIKE CONCAT('%',?,'%') OR postal_code LIKE CONCAT('%',?,'%')", search, search)


ฉันเพิ่งใช้สิ่งนี้ในแอปพลิเคชันและใช้งานได้ดี ขอบคุณจอห์น
fuzzygroup

เกี่ยวกับการฉีดยาไม่ได้และในกรณีใด ๆ สิ่งนี้ก็ไม่แตกต่างกัน สตริงถูกแทรกลงใน sql และรางควรได้รับการตรวจสอบความถูกต้องมาก่อน (ไม่สำคัญว่า a %จะต่อท้าย / ต่อท้ายหรือไม่) ไม่ว่าจะทำงานตามที่คาดไว้หรือรางมีข้อผิดพลาดที่สำคัญที่ส่งผลต่อทั้งสองกรณี
estani

5

ลอง

 def self.search(search, page = 1 )
    paginate :per_page => 5, :page => page,
      :conditions => ["name LIKE  ? OR postal_code like ?", "%#{search}%","%#{search}%"],   order => 'name'
  end

ดูเอกสารเกี่ยวกับเงื่อนไข AREL สำหรับข้อมูลเพิ่มเติม


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.