【发布时间】:2017-04-12 10:43:33
【问题描述】:
我对 postgres 还很陌生,目前使用的是 9.6。 当尝试使用它的 jsonb 文档在 postgres 中实现全文搜索时,我注意到嵌套数组的搜索结果很慢。我使用了“解释”命令,它没有使用任何索引。 为简单起见,我创建了一个表格进行调查:
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 个产品的实际数据大约需要 1200 毫秒,而其他带有索引的搜索需要 0.2 毫秒。
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,但我宁愿将所有数据保存在一个表中以提高性能。
【问题讨论】:
-
在
LATERAL JOIN中使用unnest()及其朋友(结果集生成函数,如jsonb_array_elements())可防止使用任何索引(至少在从它们计算的属性上)。如果您坚持使用这种结构,则必须创建一个自定义的IMMUTABLE函数,以从您的jsonb列中生成tsvector值,并在索引和查询中使用该函数。 -
其中有趣的部分是
tsvector没有任何内置聚合,因此您需要 1) 将名称聚合为字符串(使用一些基本规则) 2) 构建一个tsvector的自定义聚合 3) 使用巧妙的递归 CTE(因为它们已经存在连接)。
标签: arrays postgresql indexing full-text-search jsonb