web-dev-qa-db-ja.com

PostgreSQL:全文検索-部分的な単語を検索する方法は?

SQL検索メソッドの1つで速度を上げる方法についてここに投稿された質問に続いて、全文検索を利用するようにテーブルを更新するようにアドバイスされました。これは私が今やったことであり、Gistインデックスを使用して検索を高速化します。いくつかの「単純な」クエリで、私は非常に満足している著しい増加に気づきました。

しかし、部分的な単語を検索するのに苦労しています。たとえば、Word Squire(454)を含むレコードがいくつかあり、Squirrel(173)を含むレコードがいくつかあります。ここで、Squireを検索すると、454レコードのみが返されますが、Squirrelレコードも返されるようにします。

私のクエリは次のようになります

_SELECT title 
FROM movies 
WHERE vectors @@ to_tsoquery('squire');
_

to_tsquery('squire%')ができると思いましたが、うまくいきません。
部分一致を検索するにはどうすればよいですか?

また、私のデータベースには、映画などのテレビ番組だけのレコードがあります。これらは名前の上の「」で区別されます。したがって、「マンスターズ」はテレビ番組ですが、マンスターズは番組の映画です。私がやりたいのは、テレビ番組と映画だけを検索することです。これをどのように達成できるかについてのアイデアはありますか?

よろしくアントーニ

19
Anthoni Gardner

LIKEを使用しても、「squirrel」には2つの「r」があるため、squire%から「squirrel」を取得することはできません。 SquireとSquirrelを取得するには、次のクエリを実行できます。

SELECT title FROM movies WHERE vectors @@ to_tsquery('squire|squirrel');

映画とテレビ番組を区別するには、データベースに列を追加する必要があります。しかし、この猫の皮を剥ぐ方法はたくさんあります。サブクエリを使用して、postgresに最初に「squire」と「squirrel」に一致する映画を検索し、次にそのサブセットを検索して「」で始まるタイトルを検索するように強制できます。LIKE '"%...'検索で使用するインデックスを作成できます。 。

他のインデックス作成の可能性を探ることなく、これらを実行することもできます-それらをいじって、どれが最速かを見つけてください:

SELECT title 
FROM (
   SELECT * 
   FROM movies 
   WHERE vectors @@ to_tsquery('squire|squirrel')
) t
WHERE title ILIKE '"%';

または

SELECT title 
FROM movies 
WHERE vectors @@ to_tsquery('squire|squirrel') 
  AND title ILIKE '"%';
5
thetaiko

試してみてください

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire:*')

これはPostgreSQL8.4以降で機能します

49
Alexander Mera

アントーニ、

ASCIIエンコーディング(難しいかもしれませんが、私は知っています))のみを使用することを計画していると仮定すると、非常に実行可能なオプションはTrigram(pg_trgm)モジュールです: http:// www .postgresql.org/docs/9.0/Interactive/pgtrgm.html

Trigramは、GistやGinなどの組み込みのインデックス作成方法を利用します。インデックスを定義するときに行う必要がある唯一の変更は、Gist_trgm_opsまたはgin_trgm_opsのいずれかの演算子クラスを指定することです。

Contribモジュールがまだインストールされていない場合、Ubuntuでは、シェルから次のコマンドを実行するのと同じくらい簡単です。

# Sudo apt-get install postgresql-contrib

Contribモジュールが使用可能になったら、問題のデータベースにpg_trgm拡張機能をインストールする必要があります。これを行うには、モジュールをインストールするデータベースで次のPostgreSQLクエリを実行します。

CREATE EXTENSION pg_trgm;

Pg_trgm拡張機能がインストールされたら、楽しむ準備が整いました。

-- Create a test table.
CREATE TABLE test (my_column text)
-- Create a Trigram index.
CREATE INDEX test_my_colun_trgm_idx ON test USING Gist (my_column Gist_trgm_ops);
-- Add a couple records
INSERT INTO test (my_Column) VALUES ('First Entry'), ('Second Entry'), ('Third Entry')
-- Query using our new index --
SELECT my_column, similarity(my_column, 'Frist Entry') AS similarity FROM test WHERE my_column % 'Frist Entry' ORDER BY similarity DESC
31
Joshua Burns

@ alexander-meraソリューションはうまく機能します!

:スペースも必ず+に変換してください。たとえば、squire knightを検索している場合。

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire+knight:*')
6
Greg

これに対する大まかな解決策は、PGのts_rewrite関数を使用して、代替一致に対して機能するエイリアステーブルを設定することです( クエリの書き換え を参照)。これは、上記のようなケースをカバーすると同時に、tree ratの検索やsquirrelの結果の取得などのまったく異なるケースも処理します。

そのリンクで完全な詳細と説明がありますが、その要点は、次のように、2つのts_query列を持つエイリアステーブルを設定し、そのテーブルのクエリを検索に渡すことができるということです。

CREATE TABLE aliases (t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn'));

SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');

次のような最終クエリが生成されます。

WHERE vectors @@ ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases')

これはPG内のシソーラス設定に似ていますが、何かを追加するたびに完全なインデックスの再作成を必要とせずに機能します。小さなスペルのバリエーションや「これを検索すると、このような結果が期待できる」というケースに出くわすと、それらをテーブルにすばやく追加するのは非常に簡単です。 ts_rewriteに基づくクエリが、予想される2つのto_tsquery列を返す限り、そのテーブルにさらに列を追加できます。

そのドキュメントを掘り下げると、パフォーマンスチューニングの推奨例も表示されます。純粋な速度のためにトリグラムを使用することと、堅牢性のためにベクトル/クエリ/リライトを使用することの間にはバランスがあります。

3
brightball

うまくいくかもしれない1つのことはあなたが探している単語をより小さな部分に分割することです。だから、あなたはsquiまたはquirまたはsquireなどを持っているものを探すことができます...それがどれほど効率的であるかはわかりませんが、それは役立つかもしれません。

映画や映画を検索するときは、テキストを一重引用符で囲んでみてください。したがって、「show」または「show」のいずれかになります。それもうまくいくと思います。

0
John Kane