web-dev-qa-db-ja.com

Postgresqlのみで簡単なファジー検索を作成する方法は?

RoRベースのサイトの検索機能に少し問題があります。いくつかのコードを含む多くのProdutsがあります。このコードには、「AB-123-lHdfj」などの任意の文字列を使用できます。次に、ILIKE演算子を使用して製品を検索します。

Product.where("code ILIKE ?", "%" + params[:search] + "%")

正常に動作しますが、「AB123-lHdfj」や「AB123lHdfj」などのコードを持つ製品は見つかりません。

これのために何をすべきですか? postgresqlには文字列の正規化機能や他の方法がありますか? :)

33
Alve

Postgresは、soundexやmetaphoneなどの文字列比較機能を備えたモジュールを提供します。ただし、 levenshtein 編集距離関数を使用する必要があります。

Example:

test=# SELECT levenshtein('GUMBO', 'GAMBOL');
 levenshtein
-------------
           2
(1 row)

2は、2つの単語間の編集距離です。これを複数の単語に対して適用し、編集距離の結果でソートすると、探しているファジーマッチのタイプが得られます。

このクエリサンプルを試してください:(もちろん、独自のオブジェクト名とデータを使用して)

SELECT * 
FROM some_table
WHERE levenshtein(code, 'AB123-lHdfj') <= 3
ORDER BY levenshtein(code, 'AB123-lHdfj')
LIMIT 10

このクエリは言う:

コード値と入力 'AB123-lHdfj'の間の編集距離が3未満であるsome_tableからのすべてのデータの上位10の結果を教えてください。 AB123-lHdfj '...

注:次のようなエラーが表示された場合:

function levenshtein(character varying, unknown) does not exist

次を使用してfuzzystrmatch拡張機能をインストールします。

test=# CREATE EXTENSION fuzzystrmatch;
49
Paul Sasik

ポールは levenshtein() について話しました。これは非常に便利なツールですが、大きなテーブルでは非常に遅くなります。各行の検索語からレベンシュタイン距離を計算する必要があり、それは高価です。

まず、if要件が例に示されているように単純である場合でも、LIKEを使用できます。検索用語の_-_を_%_に置き換えて、WHERE句を作成します。

_WHERE code LIKE "%AB%123%lHdfj%"
_

の代わりに

_WHERE code LIKE "%AB-123-lHdfj%"
_

実際の問題がより複雑で、要件に応じてより高速なものが必要な場合、いくつかのオプションがあります。

  • もちろん、 全文検索 があります。しかし、これはあなたの場合にはやり過ぎかもしれません。

  • より可能性の高い候補は pg_trgm です。 PostgreSQL 9.1では、これをLIKEと組み合わせることができることに注意してください。こちらをご覧ください Depeszによるブログ投稿
    このコンテキストでも非常に興味深い:そのモジュールのsimilarity()関数または_%_演算子。もっと:

  • 最後になりましたが、検索する文字列をnormalizeする機能を備えた手編みのソリューションを実装できます。たとえば、_AB1-23-lHdfj_-> _ab123lhdfj_を変換し、追加の列に保存して、同じ方法で変換された検索語で検索できます。

    または、冗長列の代わりに 式のインデックス を使用します。 (関与する関数はIMMUTABLEである必要があります。)そして、おそらくそれを上記の_pg_tgrm_と組み合わせることができます。

パターンマッチング手法の概要:

39