我对postgres很新,目前使用的是9.6。 当尝试使用它的jsonb文件在postgres中实现全文搜索时,我注意到嵌套数组的搜索结果很慢。我使用'explain'命令,它没有使用任何索引。 为简单起见,我创建了一个表来调查:
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,但我宁愿将所有数据保存在一个表中以提高性能。
答案 0 :(得分:-1)
借助posz comments的提示,我找到了解决方案。 因为' ||'函数没有按照我需要的方式工作,我为tsvector使用了自定义concat函数。我在github上使用了来自glittershark的代码,并从'默认'更改了to_tsvector。到英国'以满足我的需求。
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。 我不确定这是否是最好的解决方案,所以如果你有更好的建议,请告诉我。