ndb*_*ent 5 postgresql index full-text-search json
我有一张表,用于存储两个人之间的对话。
数据将如下所示:
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;
Run Code Online (Sandbox Code Playgroud)
我从不需要单独查找每条消息,所以我只想将整个对话存储在一个jsonb字段中。我需要对所有消息执行全文搜索。
我的第一个想法是创建一个新的文本列,将所有消息连接到一个长字符串中,然后在该列上创建一个三元组 GIN 索引。
这似乎是一种浪费大量空间的 hack,所以我想避免中间列。如何直接从jsonb列创建索引?
我读这个问题的方式,你只关心message. 这里的困难是你需要,
这在函数式编程中很容易。使用 PostgreSQL 中的股票函数并不容易,并且很难使其与声明性语言一起工作。也许有一天你会有一个jsonb_array_elements(jsonb [,path])可以让你通过的,但在那之前我们可以在我们的数据库中创建一个函数。
请注意,这可能不像 plv8 函数那样快速或干净,但在下一个版本中,我们将返回一个tsvector.
这里我们使用jsonb_array_elementsjson 展开,然后将'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;
Run Code Online (Sandbox Code Playgroud)
tsvector_agg和改进我们的功能。这个函数还不是最优的,因为它返回一个字符串。然而,还有第二个困难,因为从 9.6 开始,PostgreSQL 还没有附带tsvector_agg; 但是,它是 PostgreSQL,所以我们可以制作一个..
CREATE AGGREGATE tsvector_agg (tsvector) (
SFUNC = tsvector_concat,
STYPE = tsvector
);
Run Code Online (Sandbox Code Playgroud)
这允许我们现在返回一个聚合 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;
Run Code Online (Sandbox Code Playgroud)
现在我们可以创建我们的索引..
CREATE INDEX ON FOO
USING gin (jsonb_message_to_tsvector(jsondata));
Run Code Online (Sandbox Code Playgroud)
我们会像这样查询它..
SELECT jsonb_message_to_tsvector(jsondata) @@ 'first'
FROM foo;
Run Code Online (Sandbox Code Playgroud)