2016-11-28 21 views
1

私はかなりPostgresを新しく使い、現在9.6を使用しています。 jsonbドキュメントを使用してpostgresで全文検索を実装しようとすると、ネストされた配列の検索結果が遅くなっていました。私は 'explain'コマンドを使用しましたが、インデックスを使用しませんでした。 は、単純化の目的のために私は調査するテーブルを作成しました:PostgreSQLでネストされたJSONB配列要素を検索するためのインデックス

CREATE TABLE book (
    id BIGSERIAL NOT NULL, 
    data JSONB  NOT NULL 
); 

マイ可能なインデックス:

CREATE INDEX book_author_idx 
    ON book USING GIN (to_tsvector('english', book.data ->> 'author')); 
CREATE INDEX book_author_name_idx 
    ON book USING GIN (to_tsvector('english', book.data -> 'author' ->> 'name')); 

、文書を埋めるためにいくつかのデータ:

INSERT INTO book (data) 
VALUES (CAST('{"author": [{"id": 0, "name": "Cats"}, ' || 
      '   {"id": 1, "name": "Dogs"}]}' AS JSONB)); 

を私は検索することができますよ次のクエリを使用するブック要素の場合は、インデックスを使用しません。私の実際の120k製品のデータでは約1200msかかりますが、インデックスでの他の検索は0.2msです。これとは対照的に

EXPLAIN ANALYZE 
SELECT 
    id, 
    data ->> 'author' AS author 
FROM book, jsonb_array_elements(data #> '{author}') author_array 
WHERE to_tsvector('english', author_array ->> 'name') @@ to_tsquery('cat'); 

次のクエリはbook_author_name_idxを使用していますが、理由は配列構造のものを見つけることができません。

EXPLAIN ANALYZE 
SELECT 
    id, 
    data ->> 'author' AS author 
FROM book 
WHERE to_tsvector('english', data -> 'author' ->> 'name') @@ to_tsquery('cat'); 

言語インデックスを使用するためにクエリを調整するにはどうすればよいですか? 私は著者のために新しいテーブルを作成し、IDのみを参照できることを知っていますが、パフォーマンスのためにすべてのデータを1つのテーブルに保存したいと思います。

+1

'' LATERAL JOIN'に 'unnest()'とそのフレンズ( 'jsonb_array_elements()'のような結果セット生成関数を使用すると、(少なくともそれらから計算されたプロパティの)インデックスは使用できなくなります。この構造体に固執するなら、あなたの 'jsonb'カラムから' tsvector'値を生成するためのカスタムの 'IMMUTABLE'関数を作成し、あなたのインデックスとクエリの両方でその関数を使用する必要があります。 – pozs

+0

興味深いのは、 'tsvector'には組み込みの集約がないということです。そのため、名前を文字列として集約する必要があります。(基本的なルールで)2)' tsvector'のカスタム集約を構築する3 )は巧妙な再帰的なCTEを使用します(連結がすでに存在しているため)。 – pozs

答えて

-1

私は解決策を見つけました。ポーズのヒントcomments '||'私はそれが必要な方法で動作しません、私はtsvectorsのためのカスタムコンカット関数を使用しました。私はglittersharkのコードをgithubに使い、to_tsvectorを 'default'から 'english'に変更して自分のニーズに合わせました。

CREATE OR REPLACE FUNCTION concat_tsvectors(tsv1 TSVECTOR, tsv2 TSVECTOR) 
    RETURNS TSVECTOR AS $$ 
BEGIN 
    RETURN coalesce(tsv1, to_tsvector('english', '')) 
     || coalesce(tsv2, to_tsvector('english', '')); 
END; 
$$ LANGUAGE plpgsql; 

CREATE AGGREGATE tsvector_agg (
BASETYPE = TSVECTOR, 
SFUNC = concat_tsvectors, 
STYPE = TSVECTOR, 
INITCOND = '' 
); 

ここに私が書いたカスタム機能があります。入力はJSONBのようなデータで、出力は集約された著者名を持つtsvectorです。

CREATE OR REPLACE FUNCTION author_function(
    IN data  JSONB, 
    OUT resultNames TSVECTOR 
) 
    RETURNS TSVECTOR AS $$ 
DECLARE 
    authorRecords RECORD; 
    combinedAuthors JSONB []; 
    singleAuthor JSONB; 
BEGIN 
    FOR authorRecords IN (SELECT value 
         FROM jsonb_array_elements(data #> '{author}')) 
    LOOP 
    combinedAuthors := combinedAuthors || authorRecords.value; 
    END LOOP; 
    FOREACH singleAuthor IN ARRAY coalesce(combinedAuthors, '{}') 
    LOOP 
    resultNames := concat_tsvectors(resultNames, to_tsvector('english', singleAuthor ->> 'name')); 
    END LOOP; 
END; $$ 
LANGUAGE plpgsql 
IMMUTABLE; 

次に、ブックオブジェクトのインデックスを設定しました。

CREATE INDEX book_author_function_idx 
    ON book USING GIN (author_function(book.data)); 

著者名はすでにto_tsvector(「英語」、singleAuthor)関数を使用して行ってきましたので、私はこのようにそれらを照会することができます:私の実際のデータに対する結果のクエリから行ってきましたよう

EXPLAIN ANALYSE 
SELECT 
    id, 
    data ->> 'author' AS author 
FROM book 
WHERE author_function(book.data) @@ to_tsquery('cat'); 

1100〜1200ms〜0.5ms。 これが最善の解決策であるかどうかは分かりませんので、より良い提案がある場合はお知らせください。

関連する問題