PostgreSQLにおけるファジィ探索の実装ハンドブック
ソフトウェア工学がpolyglotになったように、PostgreSQLのようなデータベースシステムはますますマルチモデルになりました.
.
Key-Value store type in PostgreSQL .
. そしてもっとたくさん.本稿では、PostgreSQLのファジーサーチを実装する方法に焦点をあてます.
我々が選択するかどうかにかかわらず
注意:これらの3つのタイプの間にパフォーマンスの違いはありません.空白のパッド型を使用した場合のストレージ領域の増加と、長さの制約を受けた列に格納するときの長さをチェックするためのいくつかの余分なCPUサイクルです.中
The
この記事のために、我々は2009年から嵐イベントデータセットを選ぶつもりですNOAA website . 簡単にするために、我々はオリジナルのデータセットが付属しているすべての列を含みません.以下は、データセットのデータベーススキーマの修正です.
対応するテーブルは、次のCREATE TABLEステートメントを使用して作成できます.
テキスト列で実行できるクエリの中で最も簡単なのは、等値演算子です.イベントタイプがAであるクエリを実行しましょう
計画は、インデックスがない場合、すべての行を検索することです.クエリプランの詳細については、そのトピックの詳細記事です.
クエリを高速化する
インデックスを作成した後、我々は速度の向上が重要です観察することができます.
The B-Tree index PostgreSQLの世界で最も単純な、まだ使用されているインデックスの一つです.しかし、テキストに関しては、それは検索を扱うことができません.以下の質問を考えてみましょう.私たちは、嵐、すなわち、冬の嵐や氷の嵐などのイベントを検索します.
インデックスを作成する場合、B - treeはまだ検索のバリエーションを処理することができます
The
注意:この結果は、結果のアイデアを与えるためだけに使用されます.
上に作成したアンカーされた検索インデックスなしで、上記のクエリはシーケンシャルスキャンを使用します.今ではインデックスを使用します.
フルテキスト検索でもインデックスを使用しようとするが、それは大幅に遅くなります.下記のクエリを考えます.
以前のアンカーされた検索(すなわち接頭辞または接尾語検索)より約3.4 x遅いです.
つは、これがシーケンシャル探索より良いと主張することができました.
しかし、これは単なる速度よりも制限があります.のようなより大きな列にパターンopsインデックスを作成してみましょう
B - treeインデックスは固有のサイズ制限(良い理由のために)を持っています.ファジィ検索の現実世界ユースケースに移りましょう.
Btreeインデックスは、より良いパフォーマンスで直接マッチまたは接頭辞/接尾辞マッチでより小さなストリングだけを検索することができます.しかし、現実世界では、ユーザーはしばしば単語を間違えます、そして、それは彼らを捜すのがかなり難しいです.これはPostgreSQLのファジーサーチ機能が入っているところです.同様の単語で文字列を検索でき、btreeインデックスのサイズ制限はありません.
trigramインデックスなしでは、可能性を介して手動で検索しない限り、可能性を検索することは実質的に不可能です.では、どのようにtrigram indexを作成できるかを見てみましょう.
The
The Levenshtein distance つの単語、すなわち、単一の文字の最小数の間の距離は、マッチするストリング/語のために必要とされる編集です.この機能を使用するには、別の拡張子を
イベントタイプに関する結果も得ることに注意してください
The
私たちのtrigramクエリをスピードアップするために使用できる特別なインデックスがあります.
我々は重要なスピードアップを見ることができます.しかし、返される行が多いので(5160)、クエリは少し長くなります.
PostgreSQLには、多くの高度な拡張機能とインデックスがあります.しかし、これらは、速度に関して独自の制限があり、英語/マルチバイト符号化以外の言語で動作しないかもしれないことを心に留めておくべきです
ここまで学んだことを要約しましょう. 直接文字列マッチ. パターンオプス trigramファジィ探索 レベンシテインマッチング 音声マッチ. 私たちは、この記事では全文検索などの記事を見ていない検索に関して、より多くの機能があります
.
Key-Value store type in PostgreSQL .
.
PostgreSQL Varlenaデータ型の紹介
我々が選択するかどうかにかかわらず
char
, varchar
or text
, PostgreSQLの使用構造は以下の通りです varlena
. スキーマをデザインしようとすると、データベースがどのように解釈しているかを理解することが重要です.以下はからの抜粋ですdocs ,注意:これらの3つのタイプの間にパフォーマンスの違いはありません.空白のパッド型を使用した場合のストレージ領域の増加と、長さの制約を受けた列に格納するときの長さをチェックするためのいくつかの余分なCPUサイクルです.中
character( n )
それは他のいくつかのデータベースシステムで性能上の利点があります.PostgreSQLにはそのような利点はありません.事実、character( n )
それは通常、その追加のストレージコストのため、3つの中で最も遅いです.たいていの場合text
or character varying
代わりに使用する必要があります.The
text
DataTypeはこの記事の主な焦点です.学習のためのデータセットの選択
この記事のために、我々は2009年から嵐イベントデータセットを選ぶつもりですNOAA website . 簡単にするために、我々はオリジナルのデータセットが付属しているすべての列を含みません.以下は、データセットのデータベーススキーマの修正です.
対応するテーブルは、次のCREATE TABLEステートメントを使用して作成できます.
CREATE TABLE storm_events (
event_id integer,
event_type text,
damage_property text,
damage_crops text,
event_source text,
flood_cause text,
episode_narrative text,
event_narrative text
)
以下のコマンドを使ってCSVをインポートできます.COPY storm_events FROM '/path/to/csv' DELIMITER ',' CSV HEADER;
Btreeインデックスを用いた簡単な探索
テキスト列で実行できるクエリの中で最も簡単なのは、等値演算子です.イベントタイプがAであるクエリを実行しましょう
Winter Storm
.SELECT
event_narrative
FROM
storm_events
WHERE
event_type = 'Winter Storm'
計画は、インデックスがない場合、すべての行を検索することです.クエリプランの詳細については、そのトピックの詳細記事です.
インデックスの作成
クエリを高速化する
40ms
行の数が少ないために、しかし、テキストが大きく、行の数の増加とともに大幅に変更されます.40ms
クエリの数が数千になり、サーバーを大幅に遅くすることができるので、リレーショナルDBSでは非常に遅いと見なされます)簡単なB - treeインデックスを作成できます.CREATE INDEX ON storm_events USING BTREE(event_type)
また、多数の行があれば、同時にインデックスを作成します.簡単なクエリの実行
インデックスを作成した後、我々は速度の向上が重要です観察することができます.
The B-Tree index PostgreSQLの世界で最も単純な、まだ使用されているインデックスの一つです.しかし、テキストに関しては、それは検索を扱うことができません.以下の質問を考えてみましょう.私たちは、嵐、すなわち、冬の嵐や氷の嵐などのイベントを検索します.
EXPLAIN ANALYZE SELECT
event_type
FROM
storm_events
WHERE
event_type LIKE '%Storm'
btreeインデックスはそのようなクエリを処理できません.また、プランナーはシーケンシャルスキャンに対応します.パターン検索
インデックスを作成する場合、B - treeはまだ検索のバリエーションを処理することができます
pattern_ops
戦略CREATE INDEX anchored_search ON storm_events (event_type text_pattern_ops)
左アンカー探索を使用する前に使用したクエリLIKE '%Storm'
インデックスを使用できます.regex検索
The
pattern_ops
正規表現でも使えます.下記のクエリを考えます.SELECT
DISTINCT(event_type)
FROM
storm_events
WHERE
event_type ~ '^(S|H)'
これはS
or H
. 注意:この結果は、結果のアイデアを与えるためだけに使用されます.
上に作成したアンカーされた検索インデックスなしで、上記のクエリはシーケンシャルスキャンを使用します.今ではインデックスを使用します.
パターンの制限
フルテキスト検索でもインデックスを使用しようとするが、それは大幅に遅くなります.下記のクエリを考えます.
SELECT
event_type
FROM
storm_events
WHERE
event_type ILIKE '%winter%'
私たちはILIKE
我々はケースを知らないので、それは混合ケースでもすることができます.また、語winter
文字列内の任意の場所に存在できます.これを実行すると、クエリはインデックスを使用しようとします.以前のアンカーされた検索(すなわち接頭辞または接尾語検索)より約3.4 x遅いです.
つは、これがシーケンシャル探索より良いと主張することができました.
しかし、これは単なる速度よりも制限があります.のようなより大きな列にパターンopsインデックスを作成してみましょう
episode_narrative
.CREATE INDEX anchored_search_full ON storm_events (episode_narrative text_pattern_ops)
これはエラーになります.B - treeインデックスは固有のサイズ制限(良い理由のために)を持っています.ファジィ検索の現実世界ユースケースに移りましょう.
PostgreSQLにおけるファジィ探索
Btreeインデックスは、より良いパフォーマンスで直接マッチまたは接頭辞/接尾辞マッチでより小さなストリングだけを検索することができます.しかし、現実世界では、ユーザーはしばしば単語を間違えます、そして、それは彼らを捜すのがかなり難しいです.これはPostgreSQLのファジーサーチ機能が入っているところです.同様の単語で文字列を検索でき、btreeインデックスのサイズ制限はありません.
trigramを用いた単語の類似性
trigramインデックスなしでは、可能性を介して手動で検索しない限り、可能性を検索することは実質的に不可能です.では、どのようにtrigram indexを作成できるかを見てみましょう.
CREATE EXTENSION pg_trgm;
この拡張機能が有効になったら、以下のようなファジー検索を行うことができます.The
SIMILARITY
関数は、いわゆるスコアを返します.我々は必要な試合に応じて、このスコアを調整することができます.デフォルトのスコアは0.3
そして、もし我々がスコアを心配していない場合は、以下の速記を使用することができます.SELECT * FROM storm_events WHERE event_type % 'Drot'
これは上記のクエリと同じです.The documentation これはかなり広範囲です、そして、拡大は他のきちんとした機能をたくさん持っています.このセクションの後のインデックスを使用してこのようなクエリを高速化する方法を参照してください.レベンシュテインマッチング
The Levenshtein distance つの単語、すなわち、単一の文字の最小数の間の距離は、マッチするストリング/語のために必要とされる編集です.この機能を使用するには、別の拡張子を
fuzzystrmatch
.CREATE EXTENSION fuzzystrmatch;
同じ文字列から4未満のlevenshtein距離を持つすべての偶数の型にマッチしましょうdrot
.SELECT * FROM storm_events WHERE levenshtein(event_type, 'Drot') < 4
イベントタイプに関する結果も得ることに注意してください
Heat
.発音試合
The
fuzzystrmatch
エクステンションは、同様のスペルを持っていないが、同じ音を検索することができます別のクールな機能を備えています.SELECT DISTINCT(event_type) FROM storm_events WHERE DIFFERENCE(event_type, 'he') > 2
これは私たちすべてのイベントのような音を与えるhe
. 高速化のインデックス
私たちのtrigramクエリをスピードアップするために使用できる特別なインデックスがあります.
CREATE INDEX trgm_idx on storm_events using gin(event_type gin_trgm_ops);
我々は重要なスピードアップを見ることができます.しかし、返される行が多いので(5160)、クエリは少し長くなります.
Postgresファジィ探索トレードオフ
PostgreSQLには、多くの高度な拡張機能とインデックスがあります.しかし、これらは、速度に関して独自の制限があり、英語/マルチバイト符号化以外の言語で動作しないかもしれないことを心に留めておくべきです
UTF-8
. しかし、これらの機能が必要な重要なギャップを埋めるが、我々は完全な検索ソリューションなどのために行くことはできませんSolr/Elastic Search . 閉鎖思考
ここまで学んだことを要約しましょう.
LIKE
and Regex
クエリ.text
, ts_vector
and jsonb
今後の記事で調査されるデータ型.Reference
この問題について(PostgreSQLにおけるファジィ探索の実装ハンドブック), 我々は、より多くの情報をここで見つけました https://dev.to/arctype/a-handbook-to-implement-fuzzy-search-in-postgresql-29laテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol