雑記

2000|01|
2003|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|
2007|01|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|09|11|
2009|02|03|05|06|07|08|10|11|12|
2010|01|03|04|05|06|07|08|09|10|
2011|05|06|09|10|
2012|03|07|09|12|
2013|01|02|04|05|07|08|10|11|
2014|04|05|08|10|12|
2015|01|05|
2016|09|

2006-02-08

BMWのWebサイトがGoogleから締め出される

あちこちで報道されているニュースですが、なぜわざわざリンクする気になったかというと、f1.racing-live.com というコンピュータ・ネットワーク系ではないニュースサイトで取り上げられていたので。Googleってもはやインフラに近い扱いですね。Google八分とかしてちゃ駄目じゃん。

本日のツッコミ(全1件) [ツッコミを入れる]

- taru_k [私の職場のWebサイト構築時でも、「いかにGoogleで上位に来る構成にするか」ってのが話題になるからね.今回みたい..]


2009-02-08

[Rails] like式とプレースホルダと'%'

Railsでlike式を使った検索を行う場合、

Foo.find(:conditions => ["text like ?", "%"+params[:word]+"%"])

という方法が常套手段のようだが、PostgreSQLな環境では、フォームのword欄に'む%り'のような文字列を入れられると、「じゅげむじゅげむごこうのすりきれ」といったエントリにもマッチしてしまった。これは非常によろしくない。

で、安易に、

Foo.find(:conditions => ["text like ?", "%"+params[:word].gsub(/%/, '\%')+"%"])

とかエスケープしようとすると、プレースホルダの変換が効いてしまい、

SELECT * From foos WHERE (text like '%む\\%り%') ...

てな具合に'\'がエスケープされたSQLが発行されてうまくいかない。

ソースを当たってActiveRecord::ConnectionAdapters::Quoting.quote_string(s)らしいという所まではつきとめ、書き換えたら一応うまくいったが、INSERTとかにも使われていそうなので、副作用が怖い。

とかなんとか半日ぐらい唸っていたら、突然へんなのが降りてきた。

まず、コントローラに以下のメソッドを追加

 def escape_for_like(s, chr)
   s.downcase.gsub(/\[/, chr+'[').gsub(/%/, chr+'%').gsub(/_/, chr+'_')
 end

で、以下のようなfindを書く

 Foo.find(:conditions => ["lower(text) like E? ESCAPE 'A'", "%"+escape_for_like(params[:word], 'A')+"%"])

解説すると、

  • lower()とdowncaseでデータと検索キーをすべて英小文字化
  • 大文字の'A'をエスケープ文字とすることでプレースホルダーでの変換を回避
  • '%', '['および'_'をエスケープしてparams[:word]内の余計な解釈を回避

というわけで、マニュアルに

  • '%', '['および'_'といった記号の検索はできません

とか、

  • 検索ではワイルドカードが使えます。'%'を入れると…

とかへんてこりんな仕様を書かずに、一言

  • 英大文字と小文字は区別されません

と書くだけで済む。かな。

追記

さらに

 def escape_for_like(s, chr)
   s.gsub(chr, chr*2).gsub(/\[/, chr+'[').gsub(/%/, chr+'%').gsub(/_/, chr+'_')
 end

で、

 Foo.find(:conditions => ["text like E? ESCAPE 'A'", "%"+escape_for_like(params[:word], 'A')+"%"])

とやれば、大文字小文字交じりでもOKだった。