前書き

フルテキスト検索(FTS)は、データベースで結果を見つけるために検索エンジンが使用する手法です。 ショップ、検索エンジン、新聞などのWebサイトでの検索結果の出力に使用できます。

具体的には、FTSは_documents_を取得します。これは、テキストデータを含むデータベースエンティティであり、検索条件に完全には一致しません。 これは、たとえば、ユーザーが「猫と犬」を検索する場合、FTSに裏付けられたアプリケーションは、単語(「猫」または「犬」のみ)を含む結果を返すことができることを意味します。 (「犬と猫」)、または単語の変形(「猫」または「犬」)が含まれています。 これにより、ユーザーが何を意味しているのかを推測し、より関連性の高い結果をより迅速に返すという点で、アプリケーションに利点がもたらされます。

技術的に言えば、PostgreSQLのようなデータベース管理システム(DBMS)は通常、LIKE句を使用した部分的なテキスト検索を許可します。 ただし、これらの要求は、大規模なデータセットではパフォーマンスが低下する傾向があります。 また、ユーザーの正確な入力との一致に制限されているため、関連情報を含むドキュメントがあっても、クエリは結果を生成しない場合があります。

FTSを使用すると、より高度なツールに余分な依存関係を導入することなく、より強力なテキスト検索エンジンを構築できます。 このチュートリアルでは、PostgreSQLを使用して架空のニュースWebサイトの記事を含むデータを保存し、FTSを使用してデータベースにクエリを実行し、最適な一致のみを選択する方法を学習します。 最後のステップとして、全文検索クエリのパフォーマンス改善を実装します。

前提条件

このガイドを始める前に、次のものが必要です。

  • このhttps://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04 [Ubuntu 16.04での初期サーバー設定]ガイド(sudo nonを含む)に従ってセットアップされた1つのUbuntu 16.04サーバー-rootユーザー。

  • https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-16-04 [Ubuntu 16.04にPostgreSQLをインストールして使用する方法]ガイドに従ってインストールされたPostgreSQL 。 このチュートリアルでは、このガイドで設定した「++」データベースとユーザーを使用します。

上記のチュートリアルに従わずにPostgreSQLサーバーをセットアップする場合、 `+ sudo apt-get list postgresql-contrib `を使用して ` postgresql-contrib +`パッケージがあることを確認してください。

手順1-サンプルデータの作成

まず、全文検索プラグインをテストするためのデータが必要なので、サンプルデータを作成しましょう。 すでにテキスト値を持つ独自のテーブルがある場合は、手順2に進んで、適切な置換を行うことができます。

それ以外の場合、最初のステップは、サーバーからPostgreSQLデータベースに接続することです。 同じホストから接続しているため、デフォルトではパスワードを入力する必要はありません。

sudo -u postgres psql sammy

これにより、操作中のデータベース名を示すインタラクティブなPostgreSQLセッションが確立されます。この例では、「+」です。 ` sammy =#+`データベースコマンドプロンプトが表示されます。

次に、データベースに「+ news +」というサンプルテーブルを作成します。 この表の各エントリは、タイトル、コンテンツ、著者名、一意の識別子を含むニュース記事を表します。

CREATE TABLE news (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  author TEXT NOT NULL
);

「+ id 」は、テーブルの自動インクリメントカウンターを作成する特別なタイプ「 SERIAL +」を持つテーブルのプライマリインデックスです。 これは、データベースインデックスに自動的に移動する一意の識別子です。 このインデックスについては、パフォーマンスの改善を検討する際にステップ3で詳しく説明します。

次に、 `+ INSERT +`コマンドを使用してテーブルにサンプルデータを追加します。 以下のコマンドのこのサンプルデータは、いくつかのサンプルニュース記事を表しています。

INSERT INTO news (id, title, content, author) VALUES
   (1, 'Pacific Northwest high-speed rail line', 'Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.', 'Greg'),
   (2, 'Hitting the beach was voted the best part of life in the region', 'Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.', 'Ethan'),
   (3, 'Machine Learning from scratch', 'Bare bones implementations of some of the foundational models and algorithms.', 'Jo');

データベースに検索するデータがあるので、クエリを作成してみてください。

ステップ2-ドキュメントの準備と検索

ここでの最初のステップは、データベーステーブルから複数のテキスト列を持つ1つのドキュメントを構築することです。 次に、結果の文字列を単語のベクトルに変換できます。これがクエリで使用されます。

まず、PostgreSQLの連結関数 `+ || `と変換関数 ` to_tsvector()+`を使用して、すべての列をまとめる必要があります。

SELECT title || '. ' || content as document, to_tsvector(title || '. ' || content) as metadata FROM news WHERE id = 1;

これにより、最初のレコードがドキュメント全体として返され、検索に使用される変換されたバージョンも返されます。

Output-[ RECORD 1 ]-----------------------------------------------------
document    | Pacific Northwest high-speed rail line. Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

metadata    | '140':18 'current':8 'high':4 'high-spe':3 'ideal':29 'line':7 'mile':19 'none':25 'northwest':2 'option':14 'pacif':1 'rail':6 'seattl':21 'speed':5 'travel':16 'vancouv':23

上記の出力では、変換されたバージョンの `+ metadata `の語数が元の ` document `の語数よりも少ないことに気付くかもしれません。 一部の単語は異なり、すべての単語にはセミコロンと数字が付加されています。 これは、関数 ` to_tsvector()+`が各単語を正規化して同じ単語の異形を検出できるようにし、結果をアルファベット順にソートするためです。 この数字は、「+ドキュメント」における世界の位置です。 正規化された単語が複数回現れる場合、追加のカンマ区切りの位置があります。

これで、「Explorations」という用語を検索することにより、この変換されたドキュメントを使用してFTS機能を利用できます。

SELECT * FROM news WHERE to_tsvector(title || '. ' || content) @@ to_tsquery('Explorations');

ここで使用した関数と演算子を調べてみましょう。

関数 `+ to_tsquery()`は、パラメータ(直接またはわずかに調整されたユーザー検索)を、 ` to_tsvector()+`と同じ方法で入力を減らすテキスト検索条件に変換します。 さらに、この関数を使用して、使用する言語と、結果にすべての単語を含める必要があるのか​​、その1つだけを含めるのかを指定できます。

「+ @@ 」演算子は、「 tsvector 」が「 tsquery 」または別の「 tsvector 」と一致するかどうかを識別します。 これは、「 true」または「+ false」を返します。これにより、「+ WHERE」基準の一部として簡単に使用できます。

Output-[ RECORD 1 ]-----------------------------------------------------
id      | 2
title   | Hitting the beach was voted the best part of life in the region
content | Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.
author  | Ethan

検索に使用した単語は「Explorations」でしたが、クエリは「Exploring」という単語を含むドキュメントを返しました。 ここでFTSの代わりに `+ LIKE +`演算子を使用すると、空の結果になります。

FTS用のドキュメントを準備する方法とクエリを構成する方法がわかったので、FTSのパフォーマンスを改善する方法を見てみましょう。

ステップ3-FTSパフォーマンスの改善

FTSクエリを使用するたびにドキュメントを生成することは、大規模なデータセットまたは小規模なサーバーを使用する場合のパフォーマンスの問題になる可能性があります。 これに対する良い解決策は、ここで実装しますが、行を挿入するときに変換されたドキュメントを生成し、他のデータとともに保存することです。 このように、毎回生成する代わりに、クエリで取得することができます。

最初に、既存の `+ news `テーブルに `+`という追加の列を作成します。

ALTER TABLE news ADD "document" tsvector;

ここで、別のクエリを使用してデータをテーブルに挿入する必要があります。 ステップ2とは異なり、ここでも変換されたドキュメントを準備し、次のように新しい「+ document +」列に追加する必要があります。

INSERT INTO news (id, title, content, author, document)
VALUES (4, 'Sleep deprivation curing depression', 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.', 'Patel', to_tsvector('Sleep deprivation curing depression' || '. ' || 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.'));

既存のテーブルに新しい列を追加するには、最初に `+ document +`列に空の値を追加する必要があります。 次に、生成された値で更新する必要があります。

`+ UPDATE +`コマンドを使用して、不足しているデータを追加します。

UPDATE news SET document = to_tsvector(title || '. ' || content) WHERE document IS NULL;

これらの行をテーブルに追加するとパフォーマンスが向上しますが、大規模なデータセットでは、データベースがテーブル全体をスキャンして検索条件に一致する行を見つける必要があるため、依然として問題が発生する可能性があります。 これに対する簡単な解決策は、インデックスを使用することです。

_データベースインデックス_は、データ取得操作のパフォーマンスを向上させるメインデータとは別にデータを格納するデータ構造です。 テーブルの内容が変更されると、追加の書き込みと比較的小さなストレージスペースのコストで更新されます。 その小さなサイズと調整されたデータ構造により、クエリの選択にメイン表スペースを使用するよりもはるかに効率的にインデックスを操作できます。

最終的に、インデックスは、特別なデータ構造とアルゴリズムを使用して検索することにより、データベースが行をすばやく見つけるのに役立ちます。 PostgreSQLには、特定の種類のクエリに適したhttps://www.postgresql.org/docs/9.1/static/indexes-types.html [いくつかの種類のインデックス]があります。 このユースケースに最も関連するのは、GiSTインデックスとGINインデックスです。 それらの主な違いは、テーブルからドキュメントを取得できる速度です。 GINは、新しいデータを追加するときはビルドが遅くなりますが、クエリは速くなります。 GISTはより高速に構築されますが、追加のデータ読み取りが必要です。

GiSTはデータの取得がGINの約3倍遅いため、ここでGINインデックスを作成します。

CREATE INDEX idx_fts_search ON news USING gin(document);

インデックス付きの + document`カラムを使用して、 + SELECT`クエリももう少しシンプルになりました。

SELECT title, content FROM news WHERE document @@ to_tsquery('Travel | Cure');

出力は次のようになります。

Output-[ RECORD 1 ]-----------------------------------------------------
title   | Sleep deprivation curing depression
content | Clinicians have long known that there is a strong link between sleep, sunlight and mood.
-[ RECORD 2 ]-----------------------------------------------------
title   | Pacific Northwest high-speed rail line
content | Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

完了したら、 `+ \ q +`でデータベースコンソールを終了できます。

結論

このガイドでは、PostgreSQLでの全文検索の使用方法について説明しました。これには、メタデータドキュメントの準備と保存、パフォーマンスの向上のためのインデックスの使用が含まれます。 PostgreSQLのFTSの詳細については、https://www.postgresql.org/docs/9.5/static/textsearch-intro.html [全文検索に関するPostgreSQLの公式ドキュメント]をご覧ください。