Rails の検索クエリで where/like 条件を変更するにはどうすればよいですか?
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
アクセントに関係なく結果が一致するようにするには?(例: メトロ = メトロ)。utf8を使っているのでto_asciiが使えません。制作は Heroku で行っています。
Rails の検索クエリで where/like 条件を変更するにはどうすればよいですか?
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
アクセントに関係なく結果が一致するようにするには?(例: メトロ = メトロ)。utf8を使っているのでto_asciiが使えません。制作は Heroku で行っています。
PostgreSQL 9.1以降では、次のことができます。
CREATE EXTENSION unaccent;
関数を提供し、unaccent()
必要なことを行います ( を除いてlower()
、必要に応じて追加で使用してください)。この拡張機能に関するマニュアルを読んでください。
アンアクセントとインデックスの詳細:
をインストールできないunacccent
が、関数を作成できる場合。ここからリストを編集し、時間をかけて追加しました。包括的ですが、完全ではありません。
CREATE OR REPLACE FUNCTION lower_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE STRICT AS
$func$
SELECT lower(translate($1
, '¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ'
, '123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz'
));
$func$;
クエリは次のように機能するはずです。
find(:all, :conditions => ["lower_unaccent(name) LIKE ?", "%#{search.downcase}%"])
左アンカー検索の場合、関数でインデックスを使用して非常に高速な結果を得ることができます。
CREATE INDEX tbl_name_lower_unaccent_idx
ON fest (lower_unaccent(name) text_pattern_ops);
次のようなクエリの場合:
SELECT * FROM tbl WHERE (lower_unaccent(name)) LIKE 'bob%';
または使用しますCOLLATE "C"
。見る:
私のように、PostgreSQL の拡張機能を追加unaccent
して Rails アプリケーションで動作させるのに問題がある人のために、作成する必要がある移行を次に示します。
class AddUnaccentExtension < ActiveRecord::Migration
def up
execute "create extension unaccent"
end
def down
execute "drop extension unaccent"
end
end
そしてもちろん、クエリrake db:migrate
で関数を使用できるようになった後:またはunaccent
unaccent(column) similar to ...
unaccent(lower(column)) ...
まず、postgresql-contrib をインストールします。次に、DB に接続して実行します。
CREATE EXTENSION unaccent;
DB の拡張機能を有効にします。
言語によっては、新しいルール ファイル (私の場合greek.rules
は にあります/usr/share/postgresql/9.1/tsearch_data
) を作成するか、既存のファイルに追加する必要がありますunaccent.rules
(非常に簡単です)。
独自の.rules
ファイルを作成する場合は、デフォルトにする必要があります。
ALTER TEXT SEARCH DICTIONARY unaccent (RULES='greek');
この変更は永続的であるため、やり直す必要はありません。
次のステップは、この関数を利用するためのメソッドをモデルに追加することです。
単純な解決策の 1 つは、モデルで関数を定義することです。例えば:
class Model < ActiveRecord::Base
[...]
def self.unaccent(column,value)
a=self.where('unaccent(?) LIKE ?', column, "%value%")
a
end
[...]
end
次に、次のように呼び出すだけです。
Model.unaccent("name","text")
モデル定義なしで同じコマンドを呼び出すと、次のように単純になります。
Model.where('unaccent(name) LIKE ?', "%text%"
注: 上記の例はテスト済みで、postgres9.1、Rails 4.0、Ruby 2.0 で動作します。
更新情報
@Henrik N のフィードバックにより、潜在的な SQLi バックドアが修正されました
StackExchange での検索に関連する 2 つの質問があります: https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search
しかし、あなたが Heroku を使用しているので、これが適切であるとは思えません (専用のデータベース プランを持っていない限り)。
SOにもこれがあります:他の特殊文字を保持しながらアクセント/発音記号を文字列から削除します。
ただし、これは、データがアクセントなしで保存されていることを前提としています。
それがあなたを正しい方向に向けることを願っています。
Foo
が検索対象のモデルでありname
、が列であると仮定します。Postgres translateと ActiveSupport のtransliterateを組み合わせる。次のようなことができます:
Foo.where(
"translate(
LOWER(name),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)