web-dev-qa-db-ja.com

複数列のPostgres全文検索、なぜ実行時ではなくインデックスで連結するのですか?

ここ数日、postgresで全文検索を行ったことがありますが、複数の列を検索するときのインデックス付けについて少し混乱しています。

Postgres docs は、次のように、連結された列にts_vectorインデックスを作成することについて話します。

CREATE INDEX pgweb_idx ON pgweb 
    USING gin(to_tsvector('english', title || ' ' || body));

私はそのように検索できます:

... WHERE 
      (to_tsvector('english', title||' '||body) @@ to_tsquery('english', 'foo'))

ただし、タイトルだけ、本文だけ、または両方を検索する場合は、3つの個別のインデックスが必要になります。そして、3列目に追加した場合、6つのインデックスになる可能性があります。

ドキュメントで見たことのない別の方法は、2つの列に別々にインデックスを付けてから、通常のWHERE...ORクエリを使用することです。

... WHERE
      (to_tsvector('english', title) @@ to_tsquery('english','foo'))
    OR
      (to_tsvector('english', body) @@ to_tsquery('english','foo'))

2つを100万行以下でベンチマークしても、基本的にパフォーマンスに違いはないようです。

だから私の質問は:

列を個別にインデックス付けするだけでなく、このようにインデックスを連結したいのはなぜですか?両方の長所と短所は何ですか?

私の推測では、事前に両方の列だけを検索したい場合(一度に1つずつではない)は、どちらが少ないメモリを使用するかを連結することで、1つのインデックスしか必要としないでしょう。

10
latentflip

いいえ、個別のインデックスは必要ありません。重み機能を使用します。これらは、あなたが照会できるラベルにすぎません。 (A-D)に対してクエリを実行する最大4つのラベルを持つことができます。

--search any "field" for quick:
select 'quick:1A brown:2B quick:3C'::tsvector @@ 'quick'::tsquery; --true

--search B "field" for quick:
select 'quick:1A brown:2B quick:3C'::tsvector @@ 'quick:B'::tsquery; --false

--search B or C "fields" for quick:
select 'quick:1A brown:2B quick:3C'::tsvector @@ 'quick:BC'::tsquery; --true

Tsvectorを連結して、重みを個別に適用してからまとめることができます。

select
  setweight( name_column::tsvector, 'A') || setweight( phone_column::tsvector, 'B');
3
Neil McGuigan

実際の代替策は、[〜#〜] or [〜#〜]を使用してwhere [〜#〜] and [〜#〜]ではなくwhereを使用することです。

Tsvector(body + title)にインデックスがあり、それを検索している場合、検索された単語はタイトル[〜#〜]または[〜#〜]の本文になります。

また、テストするときは、テーブルに適切な数の行があることを確認してください。

良い違いを示す最も単純なケース:2つの単語を見つけます-タイトルにある可能性が高い単語の1つ。と他の-それは体内にある可能性が非常に高いです。ただし、両方基準に一致する行が多くないことを確認してください。たとえば、「depesz」という単語の30%が体内にあるとします。また、タイトルに「mysql」が含まれる可能性は30%未満です。しかし、同じ行のいずれかのフィールドに「depeszとmysql」があることはほとんどありません。そして、そのようなインデックスでパフォーマンスをチェックします。

2
user1593