2人の会話を格納するテーブルがあります。
データは次のようになります。
CREATE TABLE foo
AS
SELECT $$[
{ "user": 1, "timestamp": 1, "message": "First message" },
{ "user": 2, "timestamp": 2, "message": "Second message" },
{ "user": 2, "timestamp": 3, "message": "Debounced message from same user" },
{ "user": 1, "timestamp": 4, "message": "Last message" }
]$$::jsonb AS jsondata;
各メッセージを個別に検索する必要はないので、会話全体を1つのjsonb
フィールドに格納します。すべてのメッセージに対して全文検索を実行する必要があります。
私の最初の考えは、新しいテキスト列を作成し、すべてのメッセージを1つの長い文字列に連結し、その列にトライグラムGINインデックスを作成することでした。
スペースを浪費するハックのようですので、中間のコラムは避けたいと思います。 jsonb
列から直接インデックスを作成するにはどうすればよいですか?
私がこの質問を読んだ方法では、あなたはmessage
だけを気にします。ここでの難しさは、あなたがする必要があることです、
これは関数型プログラミングでは簡単です。 PostgreSQLのストック関数を使用するのは簡単ではなく、宣言型言語で機能させることは困難です。たぶんいつかあなたはjsonb_array_elements(jsonb [,path])
を手にするでしょうが、それまではデータベースに関数を作成することができます。
これはおそらくplv8関数ほど高速でもクリーンでもありませんが、次のリビジョンではtsvector
を返します。
ここでは、jsonb_array_elements
を使用してjsonを展開し、'message'
要素を文字列に集約します。
CREATE OR REPLACE FUNCTION jsonb_message_to_string( jsondata jsonb, out string text )
AS $func$
BEGIN
SELECT INTO string
string_agg(d->>'message', ' ')
FROM jsonb_array_elements(jsondata) AS d;
RETURN;
END;
$func$ LANGUAGE plpgsql
IMMUTABLE;
tsvector_agg
の作成と機能の改善。この関数は文字列を返すため、まだ最適ではありません。ただし、9.6の時点でPostgreSQLにはtsvector_agg
がまだ付属していないという2番目の問題があります。しかし、それはPostgreSQLなので、作成できます。
CREATE AGGREGATE tsvector_agg (tsvector) (
SFUNC = tsvector_concat,
STYPE = tsvector
);
これにより、より高速で位置情報を保持する集約tsvectorを返すことができます。これで機能を改善できます。ここでは、新しいjsonb_message_to_tsvector
を作成します。
CREATE OR REPLACE FUNCTION jsonb_message_to_tsvector( jsondata jsonb, out tsv tsvector )
AS $func$
BEGIN
SELECT INTO tsv
tsvector_agg(to_tsvector(d->>'message'))
FROM jsonb_array_elements(jsondata) AS d;
RETURN;
END;
$func$ LANGUAGE plpgsql
IMMUTABLE;
これで、インデックスを作成できます。
CREATE INDEX ON FOO
USING gin (jsonb_message_to_tsvector(jsondata));
そして、そのようにクエリします。
SELECT jsonb_message_to_tsvector(jsondata) @@ 'first'
FROM foo;
ここに例があります:
t=# create table so59(j jsonb);
CREATE TABLE
t=# insert into so59 select '[
{ "user": 1, "timestamp": 1, "message": "First message" },
{ "user": 2, "timestamp": 2, "message": "Second message" },
{ "user": 2, "timestamp": 3, "message": "Debounced message from same user" },
{ "user": 1, "timestamp": 4, "message": "Last message" }
]
';
INSERT 0 1
t=# create index so60 on so59 using gin(to_tsvector('english',j::text));
CREATE INDEX
pdate: jsonb配列をテキストにストリップする簡単な関数を作成できます。例:
t=# create or replace function so61(j jsonb) returns text as
$$
with a as (select jsonb_array_elements(j)->>'message' m) select string_agg(m,',') from a;
$$ language sql;
CREATE FUNCTION
t=# select so61(j) from so59;
so61
----------------------------------------------------------------------------
First message,Second message,Debounced message from same user,Last message
(1 row)
t=# create index so61 on so59 using gin(to_tsvector('english',so61(j)));