F.17. fuzzystrmatch — 文字列の類似度と距離を決定する #

fuzzystrmatchモジュールは、文字列間の類似度や相違度を決める複数の関数を提供します。

注意

現時点で、soundexmetaphonedmetaphonedmetaphone_altは(UTF-8のような)マルチバイト符号化方式では充分に動作しません。 このようなデータにはdaitch_mokotoffまたはlevenshteinを使用してください。

このモジュールはtrustedと見なされます。つまり、現在のデータベースに対してCREATE権限を持つ非スーパーユーザがインストールできます。

F.17.1. Soundex #

Soundexシステムは、同一コードに変換することで似ているように見える名称を一致させる手法です。 これは、1880年、1900年、1910年の米国国勢調査で初めて使用されました。 Soundexは非英語圏の名称では特に有用なものではないことに注意してください。

fuzzystrmatchはSoundexコードを使用して動作する2つの関数を提供します。

soundex(text) returns text
difference(text, text) returns int

soundex関数は文字列をSoundexコードに変換します。 difference関数は2つの文字列をそのSoundexコードに変換し、コード位置が一致する個数を報告します。 Soundexコードは4文字からなりますので、結果は0から4までの範囲になります。 0はまったく一致しないことを、4は完全に一致することを示します。 (したがってこの関数の名前は間違っています。similarityの方がより優れた名前だったかもしれません。)

以下に使用例をいくつか示します。

SELECT soundex('hello world!');

SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');

CREATE TABLE s (nm text);

INSERT INTO s VALUES ('john');
INSERT INTO s VALUES ('joan');
INSERT INTO s VALUES ('wobbly');
INSERT INTO s VALUES ('jack');

SELECT * FROM s WHERE soundex(nm) = soundex('john');

SELECT * FROM s WHERE difference(s.nm, 'john') > 2;

F.17.2. Daitch-Mokotoff Soundex #

従来Soundexシステムと同様に、Daitch-Mokotoff Soundexは、似たような名称を同じコードに変換することで一致させます。 ただし、Daitch-Mokotoff Soundexは、非英語圏の名称に対しては、従来システムよりもはるかに便利です。 従来システムに対する主な改善点は以下のとおりです。

  • コードは4文字ではなく意味のある最初の6文字に基づいています。

  • 文字または文字の組み合わせは、7つではなく10の可能なコードにマップされます。

  • 2つの連続した文字が1つの音を持つ場合、それらは1つの数字としてコード化されます。

  • 文字または文字の組み合わせには異なる音がある場合、すべての可能性をカバーするために複数のコードが出力されます。

この関数は入力に対するDaitch-Mokotoff soundexコードを生成します。

daitch_mokotoff(source text) returns text[]

結果は、考えられる発音がいくつあるかによって1つ以上のコードを含む可能性があるため、配列として表現されます。

Daitch-Mokotoff soundexコードは6桁の数字のみで構成されるため、sourceは単語または名前であることが好ましいです。

以下に例をいくつか示します。

SELECT daitch_mokotoff('George');
 daitch_mokotoff
-----------------
 {595000}

SELECT daitch_mokotoff('John');
 daitch_mokotoff
-----------------
 {160000,460000}

SELECT daitch_mokotoff('Bierschbach');
                      daitch_mokotoff
-----------------------------------------------------------
 {794575,794574,794750,794740,745750,745740,747500,747400}

SELECT daitch_mokotoff('Schwartzenegger');
 daitch_mokotoff
-----------------
 {479465}

単一の名前の一致には、返されたテキスト配列を直接&&演算子を使用して一致させられます。 重複はマッチとみなされます。 効率のためにGINインデックスを使用できます。 第70章および以下の例を参照してください。

CREATE TABLE s (nm text);
CREATE INDEX ix_s_dm ON s USING gin (daitch_mokotoff(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('Schwartzenegger'),
  ('John'),
  ('James'),
  ('Steinman'),
  ('Steinmetz');

SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Swartzenegger');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jane');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jens');

任意の順序で任意の数の名前のインデックス付けと一致には、全文検索機能を使用できます。 第12章および以下の例を参照してください。

CREATE FUNCTION soundex_tsvector(v_name text) RETURNS tsvector
BEGIN ATOMIC
  SELECT to_tsvector('simple',
                     string_agg(array_to_string(daitch_mokotoff(n), ' '), ' '))
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE FUNCTION soundex_tsquery(v_name text) RETURNS tsquery
BEGIN ATOMIC
  SELECT string_agg('(' || array_to_string(daitch_mokotoff(n), '|') || ')', '&')::tsquery
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE TABLE s (nm text);
CREATE INDEX ix_s_txt ON s USING gin (soundex_tsvector(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('John Doe'),
  ('Jane Roe'),
  ('Public John Q.'),
  ('George Best'),
  ('John Yamson');

SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('jane doe');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john public');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('besst, giorgio');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('Jameson John');

インデックス再チェック中にsoundexコードの再計算を避ける場合は、式のインデックスの代わりに別の列のインデックスを使用できます。 これには、格納された生成列を使用できます。 5.3を参照してください。

F.17.3. レーベンシュタイン(Levenshtein) #

この関数は2つの文字列間のレーベンシュタイン距離(Levenshtein distance)を計算します。

levenshtein(source text, target text, ins_cost int, del_cost int, sub_cost int) returns int
levenshtein(source text, target text) returns int
levenshtein_less_equal(source text, target text, ins_cost int, del_cost int, sub_cost int, max_d int) returns int
levenshtein_less_equal(source text, target text, max_d int) returns int

sourceおよびtargetは255文字までの任意の非NULL文字列を取ることができます。 コストパラメータはそれぞれ、文字の挿入、削除、置換に負わせる文字数を指定します。 この関数の2番目のバージョンのようにコストパラメータを省略することができます。 この場合デフォルトですべて1になります。

levenshtein_less_equalは小さな距離だけを問題にする場合についてのlevenshtein関数の高速化版です。 実際の距離がmax_d以下の場合、levenshtein_less_equalは正しい値を返しますが、そうでなければ、max_dより大きい何らかの値を返します。 max_dが負の場合は、levenshteinと同じ動作になります。

以下に例を示します。

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

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

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 2);
 levenshtein_less_equal
------------------------
                      3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 4);
 levenshtein_less_equal
------------------------
                      4
(1 row)

F.17.4. Metaphone #

Metaphoneは、Soundex同様、入力文字に対する対応するコードを構築するという考えに基づいたものです。 2つの文字列が同一コードを持つ場合、類似とみなされます。

以下の関数は入力文字列に対するmetaphoneコードを計算します。

metaphone(source text, max_output_length int) returns text

sourceは255文字までの非NULL文字列を取ることができます。 max_output_lengthは出力metaphoneコードの最大長を設定します。 出力は長すぎるとこの長さに切り詰められます。

以下に例を示します。

test=# SELECT metaphone('GUMBO', 4);
 metaphone
-----------
 KM
(1 row)

F.17.5. Double Metaphone #

Double Metaphoneシステムは与えられた入力文字列に対する、primaryalternateという2つの似たように見える文字列を計算します。 ほとんどの場合、これらは同じですが、英語以外の名称では特に発音に応じて多少異なる場合があります。 以下の関数はprimaryコードとalternateコードを計算します。

dmetaphone(source text) returns text
dmetaphone_alt(source text) returns text

入力文字列長に関する制限はありません。

以下に例を示します。

test=# SELECT dmetaphone('gumbo');
 dmetaphone
------------
 KMP
(1 row)