Arrays 用于在PostgreSQL中搜索嵌套JSONB数组元素的索引
我对博士后是个新手,目前正在使用9.6。 当我试图在postgres中使用jsonb文档实现全文搜索时,我注意到嵌套数组的搜索结果很慢。我使用了“explain”命令,它没有使用任何索引。 为了简单起见,我创建了一个表来调查:Arrays 用于在PostgreSQL中搜索嵌套JSONB数组元素的索引,arrays,postgresql,indexing,full-text-search,jsonb,Arrays,Postgresql,Indexing,Full Text Search,Jsonb,我对博士后是个新手,目前正在使用9.6。 当我试图在postgres中使用jsonb文档实现全文搜索时,我注意到嵌套数组的搜索结果很慢。我使用了“explain”命令,它没有使用任何索引。 为了简单起见,我创建了一个表来调查: CREATE TABLE book ( id BIGSERIAL NOT NULL, data JSONB NOT NULL ); 我的可用索引: CREATE INDEX book_author_idx ON book USING GIN (t
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));
我可以使用下面的查询搜索book元素,但是它不使用任何索引。根据我的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\u author\u name\u idx,但由于数组结构的原因,没有找到任何内容
EXPLAIN ANALYZE
SELECT
id,
data ->> 'author' AS author
FROM book
WHERE to_tsvector('english', data -> 'author' ->> 'name') @@ to_tsquery('cat');
如何调整查询以使用语言索引?
我知道,我可以为作者创建一个新表,并且只引用ID,但为了提高性能,我宁愿将所有数据保存在一个表中。根据posz的提示,我找到了一个解决方案。
因为“| |”函数不能按我需要的方式工作,所以我为tsvectors使用了一个自定义concat函数。我使用了github上的代码,并将_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('english',singleAuthor)函数,因此我可以这样查询:
EXPLAIN ANALYSE
SELECT
id,
data ->> 'author' AS author
FROM book
WHERE author_function(book.data) @@ to_tsquery('cat');
因此,对我的实际数据的查询从1100-1200ms到~0.5ms。
我不确定这是否是最好的解决方案,因此如果您有更好的建议,请让我知道。根据posz的提示,我找到了一个解决方案。
因为“| |”函数不能按我需要的方式工作,所以我为tsvectors使用了一个自定义concat函数。我使用了github上的代码,并将_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('english',singleAuthor)函数,因此我可以这样查询:
EXPLAIN ANALYSE
SELECT
id,
data ->> 'author' AS author
FROM book
WHERE author_function(book.data) @@ to_tsquery('cat');
因此,对我的实际数据的查询从1100-1200ms到~0.5ms。
我不确定这是否是最好的解决方案,因此如果您有更好的建议,请让我知道。在
横向联接中使用unnest()
及其朋友(结果集生成函数,如jsonb\u array\u elements()
)可以防止使用任何索引(至少在根据它们计算的属性上)。如果您坚持使用此结构,则必须创建一个自定义的,不可变的
函数,从jsonb
列生成tsvector
值,并在索引和查询中使用该函数。有趣的是,tsvector
没有任何内置聚合,因此,您需要1)将名称聚合为字符串(使用一些基本规则)2)为tsvector
3)构建自定义聚合3)使用巧妙的递归CTE(因为它们已经存在连接)。使用unnest()
及其好友(结果集生成函数,如jsonb\u array\u elements()
)在横向连接中
禁止使用任何索引(至少在根据索引计算的属性上)。如果您坚持使用此结构,则必须创建一个自定义的,不可变的
函数,从jsonb
列生成tsvector
值,并在索引和查询中使用该函数。有趣的是,tsvector
没有任何内置聚合,因此,您需要1)将名称聚合为字符串(使用一些基本规则)2)为tsvector
3)构建自定义聚合使用一个巧妙的递归CTE(因为它们已经存在串联)。