Rails 4 LIKEクエリ-ActiveRecordは引用符を追加します


127

私はそのようなクエリを実行しようとしています

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を使用していますが、どちらも使用したことがないので、それがactiverecordなのか、postgresなのか不明です。

これをどのように設定すれば'%my_search%'、最後のクエリでいいのですか?

回答:


228

プレースホルダーは文字列に置き換えられ、正しく処理されていません。

交換する

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

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

6
これはSQLインジェクションに対して脆弱ではありませんか?つまり、これらのsearch文字列はサニタイズされていますか?
jdscosta91 2014年

6
@ jdscosta91 ?消毒処理を行う場所
house9 '10 / 10/25

7
この方法では%、@ house9のインスタンスと_内部searchはサニタイズされません。
バリーケリー

8
「?」を使用する場合 このようにレールでは、パラメータ化されたクエリに変換されます。パラメータ内のデータはサニタイズされていません(%)。ただし、クエリからコンテキストを変更して、処理されたSQLステートメントに変換することはできません。
David Hoelzer、2015

50

conditionsRails 2 の構文を使用する代わりに、代わりにRails 4のwhereメソッドを使用します。

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名前に使用-LIKEのpostgres大文字小文字を区別しないバージョン


これはまだRails 5で有効ですか?私がしている場合のでMovie.where("title ILIKE :s", s: search_string)、それが翻訳さSELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1何パーセント記号がILIKE後に存在しないことをしてくださいお知らせ) - ActiveRecordの(Railsの5.1.6)によって
sekmo

@sekmo-動作するはずですが、パーセンテージはsearch_string変数に入ります。私が思うにSELECT 1 AS one、出力のみレールコンソールであるか、使用していますかlimit(1)?参考:Rails 5.1.6にはセキュリティ上の問題があります。代わりに5.1.6.2または5.1.7を使用してください
house9 '20

22

文字列補間は機能しますが、質問でレール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

テストされていませんが、次のようなものである可能性があります:scope search_attributes、->(query、attributes){arel_attributes = attributes.map {| a | arel_table [a]} arel_queries = arel_attributes.map {| a | a.matches(query)} return where(arel_queries.reduce {| res、q | res.or q})}
Pedro Rolo

8

ActiveRecordは、によって参照されるパラメーター?が文字列であることを認識するのに十分なほど巧妙なので、一重引用符で囲みます。ある投稿が示唆しているように、Ruby文字列補間を使用して、必要な記号で文字列を埋め込むことができ%ます。ただし、これによりSQLインジェクションが発生する可能性があります(これは悪いことです)。次のように、SQL CONCAT()関数を使用して文字列を準備することをお勧めします。

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


これをアプリケーションに実装したところ、うまくいきました。ジョンありがとう
fuzzygroup 2017

注射に関しては、そうではありませんし、どんな場合でも違いはありません。文字列はsqlに挿入され、railsはそれを以前に検証する必要があります(a %が追加/追加されているかどうかは関係ありません)。期待どおりに機能するか、レールに両方のケースに影響する大きなバグがあるかのどちらかです。
エスタニ

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条件に関するドキュメントをご覧ください。


-1
.find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.