Создание индекса для полнотекстового поиска на Django
Я реализую полнотекстовый поиск в блоге, используя Django 3.2 и PostgreSQL 12.8. У меня есть база данных с 3.000 постов, и моя панель поиска ищет по post_title
, post_subtitle
и post_text
. Этот поиск имеет вес, ранжирован и постранично. Поиск работает как шарм, но он несколько медленный. Django выполняет следующий точный запрос:
SELECT "core_post"."id", "core_post"."blog_name",
"core_post"."post_url", "core_post"."post_title", "core_post"."post_subtitle",
"core_post"."post_text",
ts_rank(((setweight(to_tsvector(COALESCE("core_post"."post_title", '')), 'A') ||
setweight(to_tsvector(COALESCE("core_post"."post_subtitle", '')), 'B')) ||
setweight(to_tsvector(COALESCE("core_post"."post_text", '')), 'C')),
plainto_tsquery('Angel'))
AS "rank" FROM "core_post" WHERE
ts_rank(((setweight(to_tsvector(COALESCE("core_post"."post_title", '')), 'A') ||
setweight(to_tsvector(COALESCE("core_post"."post_subtitle", '')), 'B')) ||
setweight(to_tsvector(COALESCE("core_post"."post_text", '')), 'C')),
plainto_tsquery('Angel')) >= 0.3
ORDER BY "rank" DESC LIMIT 15
Когда я explain analyse
его, я получаю следующее:
Limit (cost=26321.90..26323.63 rows=15 width=256) (actual time=662.709..664.002 rows=15 loops=1)
-> Gather Merge (cost=26321.90..26998.33 rows=5882 width=256) (actual time=662.706..663.998 rows=15 loops=1)
Workers Planned: 1
Workers Launched: 1
-> Sort (cost=25321.89..25336.60 rows=5882 width=256) (actual time=656.142..656.144 rows=12 loops=2)
Sort Key: (ts_rank(((setweight(to_tsvector((COALESCE(post_title, ''::character varying))::text), 'A'::"char") || setweight(to_tsvector(COALESCE(post_subtitle, ''::text)), 'B'::"char")) || setweight(to_tsvector(COALESCE(post_text, ''::text)), 'C'::"char")), plainto_tsquery('Angel'::text))) DESC
Sort Method: top-N heapsort Memory: 33kB
Worker 0: Sort Method: top-N heapsort Memory: 32kB
-> Parallel Seq Scan on core_post (cost=0.00..25177.58 rows=5882 width=256) (actual time=6.758..655.854 rows=90 loops=2)
Filter: (ts_rank(((setweight(to_tsvector((COALESCE(post_title, ''::character varying))::text), 'A'::"char") || setweight(to_tsvector(COALESCE(post_subtitle, ''::text)), 'B'::"char")) || setweight(to_tsvector(COALESCE(post_text, ''::text)), 'C'::"char")), plainto_tsquery('Angel'::text)) >= '0.3'::double precision)
Rows Removed by Filter: 14910
Planning Time: 0.345 ms
Execution Time: 664.065 ms
Я не очень хорошо разбираюсь в SQL или PostgreSQL, но я пытался создать индекс, как показано в docs:
create index search_view_idx
on core_post
using gin(
ts_rank(((setweight('english', to_tsvector(COALESCE("core_post"."post_title", '')), 'A') ||
setweight('english', to_tsvector(COALESCE("core_post"."post_subtitle", '')), 'B')) ||
setweight('english', to_tsvector(COALESCE("core_post"."post_text", '')), 'C'))
));
но я получаю ошибку:
SQL Error [42883]: ERROR: function setweight(unknown, tsvector, unknown) does not exist
Type: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 64
Мое предположение заключается в том, что я не знаю, как правильно создать индекс.
Как я могу создать индекс для этого запроса Django в PostgreSQL?
Индекс поддерживает запросы @@, а не ts_rank. Это нормально, потому что вы должны проверить, что @@ соответствует, прежде чем пытаться вычислить ранг.
SELECT "core_post"."id", "core_post"."blog_name",
"core_post"."post_url", "core_post"."post_title", "core_post"."post_subtitle",
"core_post"."post_text",
ts_rank(((setweight(to_tsvector(COALESCE("core_post"."post_title", '')), 'A') ||
setweight(to_tsvector(COALESCE("core_post"."post_subtitle", '')), 'B')) ||
setweight(to_tsvector(COALESCE("core_post"."post_text", '')), 'C')),
plainto_tsquery('Angel'))
AS "rank" FROM "core_post" WHERE
to_tsvector('english', COALESCE("core_post"."post_title", '') ||
to_tsvector('english', COALESCE("core_post"."post_subtitle", '') ||
to_tsvector('english', COALESCE("core_post"."post_text", '')
@@plainto_tsquery('Angel')
ORDER BY "rank" DESC LIMIT 15
Это вернет ранги меньше 0.3, пока не будет более 15 рангов, которые выше этого. Вы можете добавить еще код для фильтрации этих рангов, но на самом деле 0.3 - это очень много, поэтому я думаю, что лучше этого не делать. Или вы можете вообще ничего не найти, даже если у вас есть разумные совпадения.
BTW, похоже, что у вас гораздо больше 3000 сообщений. Возможно, на самом деле 30,000.