web-dev-qa-db-ja.com

カスタムオペレーターのインデックスサポートのためのPostgresqlオペレータークラス

カスタムオペレーターを使用するクエリをサポートするインデックスを作成しようとしています。これはPostgresql 10.4にあります

カスタムオペレーター

this SO回答)のヒントに従って、テキスト配列内の要素に対して「LIKE」スタイルのマッチングを実行する演算子を作成しました。

CREATE FUNCTION reverse_like (text, text) returns boolean language sql 
as $$ select $2 like $1 $$;

CREATE OPERATOR <~~ ( function =reverse_like, leftarg = text, rightarg=text );

上記の演算子を使用すると、次のようなことができます

SELECT 'ab%' <~~ ANY('{"abc","def"}');

スキーマ、インデックス、クエリ

配列列を含むsessionsというWebトラフィックアクセスのテーブルがあります。

CREATE TABLE sessions
(
   session_id    varchar(24) NOT NULL,
   first_seen    timestamp,
   domains       varchar[]
);

ドメイン列をクエリして、特定のドメイン(または部分的/ワイルドカードドメイン名)にアクセスしたかどうかを確認するには、次のようにします。

SELECT count(*)
FROM session_4070ba14_f081_41cb_9ef7_9dd385934da7
WHERE 'www.foo%' <~~ ANY(domains);

上記のクエリをGINインデックスで高速化したいと思います。だから私は次のようにインデックスを作成しました:

CREATE INDEX idx_domains ON session USING GIN(domains);

質問

テーブルで分析を実行した後、set enable_seqscan = false; postgresにこのインデックスを採用させることができません。常にseqscanを実行しています。 @>のような配列演算子の上記のインデックスを使用しますが、カスタムの<~~演算子には使用しません。

これは、GINインデックスがカスタムオペレーターの処理方法を認識していないためだと思います。オペレータークラスを作成し、それを使用して私のインデックスを作成する必要がありますか?または、関数インデックスを作成しますか?

2
maxTrialfire

GINインデックスについて考えることで、これを複雑にしてしまいました。配列全体のBツリーインデックスは正常に機能し、カスタムの<~~演算子をサポートします。


CREATE INDEX IF NOT EXISTS idx_domains2 ON session(domains );

select count(*)
from session
where 'www.foo%' <~~ ANY(domains);

Finalize Aggregate  (cost=331523.11..331523.12 rows=1 width=8)
  ->  Gather  (cost=331522.90..331523.11 rows=2 width=8)
        Workers Planned: 2
        ->  Partial Aggregate  (cost=330522.90..330522.91 rows=1 width=8)
              ->  Parallel Index Only Scan using idx_domains2 on session  (cost=0.42..330200.52 rows=128952 width=0)
                    Filter: ('www.foo%'::text <~~ ANY ((domains)::text[]))
0
maxTrialfire

次のような式にインデックスを付けることはできません。

<constant><operator> ANY(<array column>)

唯一のチャンスは、式が次のようになるように演算子を定義することです。

<array column><operator><constant>

しかし、GIN演算子クラスを作成するということは、Cで拡張機能を作成することを意味します。それほど遠くに行きたくないと思います。

簡単な解決策は、そのようなことのために配列を使用しないようにデータモデルを変更することです。

0
Laurenz Albe

トライグラムのサポートについては、 parray_gin拡張 を試すことができます

WHERE domains @@> ARRAY['www.foo%'];

プレフィックスマッチングを実行するだけの場合(trigramで提供されるものよりも効率的)、ピースを結合するCコードを記述せずにこれを行う方法はないと思います。次に、配列型を直接操作するので、ANYは必要なく、reverse_like演算子のメリットもまったくないと思います。

0
jjanes