2016-12-08 5 views
2

В настоящее время я оптимизирую свои результаты поиска на jsonb-полях PostgreSQL. Я использую Postgres 9.6. Моя конечная цель - поиск по нескольким полям в моем jsonb-документе и ранжирование результатов в соответствии с их итогами во всех полях. Но я застрял, потому что функция ts_rank не использует мой индекс и сильно замедляет поиск. Вот минимальный пример:Индекс для ранжирования результатов поиска JSONB в PostgreSQL

CREATE TABLE book (
    id BIGSERIAL NOT NULL, 
    data JSONB  NOT NULL 
); 

CREATE INDEX book_title_idx 
    ON book USING GIN (to_tsvector('english', book.data ->> 'title')); 

INSERT INTO book (data) 
VALUES (CAST('{"title": "Cats"}' AS JSONB)); 

При попытке поиска в поле заголовка я использую этот запрос:

EXPLAIN ANALYZE 
SELECT * 
FROM (
     SELECT 
     id, 
     data ->> 'title'    AS title, 
     ts_rank(title_query, 'cat:*') AS score 
     FROM 
     book, 
     to_tsvector('english', data ->> 'title') title_query 
     WHERE title_query @@ to_tsquery('cat:*') 
     ORDER BY score DESC) a 
WHERE score > 0 
ORDER BY score DESC; 

Без рейтинга поиска на моих реальных данных занимает < 1мса, с ранжированием это ~ 1800 мс. Это становится хуже, чем больше полей, которые я ищу. Мне нужен рейтинг только для того, чтобы сделать хиты в нескольких полях более ценными.

ответ

2

Ваш запрос дает план (на тестовом наборе данных с 500000 строк):

                QUERY PLAN                 
--------------------------------------------------------------------------------------------------------------------------------------------- 
Sort (cost=216058.57..217308.57 rows=500001 width=63) (actual time=831.033..831.033 rows=1 loops=1) 
    Sort Key: (ts_rank(title_query.title_query, '''cat'':*'::tsquery)) DESC 
    Sort Method: quicksort Memory: 25kB 
    -> Nested Loop (cost=0.25..149927.55 rows=500001 width=63) (actual time=4.410..830.950 rows=1 loops=1) 
     -> Seq Scan on book (cost=0.00..8677.01 rows=500001 width=31) (actual time=0.024..30.159 rows=500001 loops=1) 
     -> Function Scan on to_tsvector title_query (cost=0.25..0.52 rows=1 width=32) (actual time=0.001..0.001 rows=0 loops=500001) 
       Filter: ((ts_rank(title_query, '''cat'':*'::tsquery) > '0'::double precision) AND (title_query @@ to_tsquery('cat:*'::text))) 
       Rows Removed by Filter: 1 
Planning time: 37.211 ms 
Execution time: 831.279 ms 
(10 rows) 

Replace псевдоним title_query в WHERE предложении с выражением используется в определении индекса:

EXPLAIN ANALYZE 
SELECT * 
FROM (
     SELECT 
     id, 
     data ->> 'title'    AS title, 
     ts_rank(title_query, 'cat:*') AS score 
     FROM 
     book, 
     to_tsvector('english', data ->> 'title') title_query 
     WHERE to_tsvector('english', data ->> 'title') @@ to_tsquery('cat:*') 
     ORDER BY score DESC) a 
WHERE score > 0 
ORDER BY score DESC; 

Sort (cost=9905.39..9930.39 rows=10000 width=63) (actual time=1.069..1.069 rows=1 loops=1) 
    Sort Key: (ts_rank(title_query.title_query, '''cat'':*'::tsquery)) DESC 
    Sort Method: quicksort Memory: 25kB 
    -> Nested Loop (cost=114.00..9241.00 rows=10000 width=63) (actual time=1.049..1.050 rows=1 loops=1) 
     -> Bitmap Heap Scan on book (cost=113.75..8940.75 rows=10000 width=31) (actual time=0.052..0.052 rows=1 loops=1) 
       Recheck Cond: (to_tsvector('english'::regconfig, (data ->> 'title'::text)) @@ to_tsquery('cat:*'::text)) 
       Heap Blocks: exact=1 
       -> Bitmap Index Scan on book_title_idx (cost=0.00..111.25 rows=10000 width=0) (actual time=0.047..0.047 rows=1 loops=1) 
        Index Cond: (to_tsvector('english'::regconfig, (data ->> 'title'::text)) @@ to_tsquery('cat:*'::text)) 
     -> Function Scan on to_tsvector title_query (cost=0.25..0.27 rows=1 width=32) (actual time=0.994..0.994 rows=1 loops=1) 
       Filter: (ts_rank(title_query, '''cat'':*'::tsquery) > '0'::double precision) 
Planning time: 0.639 ms 
Execution time: 1.120 ms 
(13 rows) 
+0

это большое улучшение +1 – e4c5

+0

Это действительно наш поиск по 3 полям с 2000 мс до 500 мс. Я чувствую, что могу улучшить еще дальше. Я отправлю дополнительный вопрос для моего конкретного случая, так как вы ответили на мой оригинальный вопрос. Спасибо – SlideM

+0

@ user7220980 вам, вероятно, не нужен фильтр 'score> 0', потому что вы уже выполняете соответствие FTS с' to_tsvector (...) @@ to_tsquery (...) '. Таким образом, нет необходимости использовать подзапрос. (но это, похоже, не является таким улучшением). – pozs

Смежные вопросы