Обновить

Быстрее пули: как найти счастье с PostgreSQL

Уровень сложностиПростой
Время на прочтение24 мин
Количество просмотров18K
Всего голосов 29: ↑28 и ↓1+37
Комментарии11

Комментарии 11

Почему именно postgresql выбран для полнотекстового поиска? Раньше он был одним из самых медленных движков полнотекстового поиска. Как в 2024 году он себя показывает в сравнении с elasticsearch, sphinx и т.д.?

на самом деле сравнивать postgresql со специализированным поисковым движком бессмысленно, понятно, что в последнем и скорость и функциональность в среднем будет больше/лучше. Но! Если вы своем приложении используете не самые сложные процессы полнотекстового поиска, то постгрес будет норм выбором, потому что
1/ не потребуется городить доп инфру
2/ не потребуется городить CDC процесс в поисковой движок
3/ не потребуется решать проблемы с нарушением консистентности по сути двух отдельных баз данных (постгреса и, например, эластика)
4/ все будет в одном месте с понятным единообразным апи и простым процессом интеграции в ваше приложение.

Естественно, окончательное решение нужно будет принимать оглядываясь на циферки: нормально ли все фт и нфт закрыты, но для старта постгрес будет очень даже нормальным выбором, как показывает практика внедрения в проектах вокруг (например, Discourse и gitlab).

У эластика в последних версиях вроде бы нет русской морфологии, и плагин с ней не поддерживается. Если я неправ, подскажите как ее подключить.

Так это стеммеры, прям в документации указано:

"type": "stemmer",

А стеммер не учитывает морфологию и не приводит к нормальной форме, просто сокращает по эвристикам. В реальном применении зачастую бесполезен и неточен.

ну кроме стандартного стеммера для русского языка доступен как минимум сноубол

"russian_snowball": {
  "type":     "snowball",
  "language": "Russian"
}

ну естественно стеммер с переопределением. Как мне кажется по сравнению с предыдущими версиями эластика ничего не изменилось. Ну и опять же можно плагин установить, который улучшит морфологический анализ для русского языка

https://github.com/nickyat/elasticsearch-analysis-morphology?tab=readme-ov-file

возможно его потребуется пересобрать под более свежую версию эластика (там в зависимостях стоит Elasticsearch 8.9.1) 

И в Dockerfile описано как собранный плагин подключить к Elasticsearch


Если стандартного анализатора русского языка не хватает, то можно попробовать вот этот плагин взять

https://github.com/nickyat/elasticsearch-analysis-morphology?tab=readme-ov-file

возможно его потребуется пересобрать под более свежую версию эластика (там в зависимостях стоит Elasticsearch 8.9.1)

И в Dockerfile описано как собранный плагин подключить к Elasticsearch

Вопрос не ко мне, но попробую оветить за автора) На этот счет уже были хорошие публикации. Раз и два.

В принципе сложно что-то добавить к статье Егора Рогова, учитывая, что он непосредственно общается с людьми, которые этот индекс спроектировали и знает, как все утроено изнутри :)
https://habr.com/ru/companies/postgrespro/articles/452116/,

Но может какие-то отдельные мои комментарии вам помогут.

Например, в наших задачах RUM предпочтительнее, поскольку нам важно делать ранжирование результатов поисковой выдачи по релевантности при полнотекстовом поиске, используя GIN вы в таких запросах преимуществ никаких не получаете, а вот с RUM все значительно шустрее происходит.

Скажем тут

CREATE TABLE articles (
    id serial PRIMARY KEY,
    content text,
    created_at timestamp
);

CREATE INDEX articles_content_rum_idx ON articles USING rum (to_tsvector('english', content) rum_tsvector_ops);

-- Получаем топ-5 статей по ключевым словам "AI" и "ML"
SELECT id, content
FROM articles
WHERE to_tsvector('english', content) @@ to_tsquery('AI & ML')
ORDER BY content <=> to_tsquery('AI & ML')
LIMIT 5;

RUM индекс выполняет index_scan, чтобы сразу отсортировать и вернуть 5 самых релевантных записей. Для GIN индексам потребовался бы полный скан и сортировка после поиска.

Еще комментируя статью Егора, наверное, отмечу, что у него упоминается возможность использовать RUM для исключающих ограничений

> Note that, unlike GIN, RUM supports index scan — otherwise, it would not have been possible to return exactly the required number of results in queries with «limit» clause. There is no need for the counterpart of «gin_fuzzy_search_limit» parameter accordingly. And as a consequence, the index can be used to support exclusion constraints.
Но я кроме как простого полного сравнения tsvector примера придумать не смог (( То есть, например как GIST использовать RUM не получиться, что исключать какие-то куски текста, например

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
    EXCLUDE USING rum (tsv WITH =)
);


В данном примере мы создаем исключающее ограничение на поле tsv с использованием индекса RUM. Оператор WITH = указывает, что мы хотим исключить случаи, когда значение tsv совпадает у двух разных записей. Индекс RUM здесь необходим, поскольку он поддерживает операцию равенства на типе tsvector и может эффективно выполнять такое сравнение благодаря возможностям индексного сканирования.


Мы у себя в проекте используем полнотекстовый поиск, и могу добавить на собственном опыте два самых неудобных момента при его использовании:

1) Словари считываются каждую (!) сессию что очень замедляет запросы при больших словарях. Проблема решаема с помощью shared_lspell, но его надо компилировать. Почему это нет "из коробки" - для меня загадка.

2) Так как словари - это, по сути, файлы конфигурации то вносить слова и синонимы очень неудобно. Притом у нас БД - храни все словари в табличке, да радуйся, но нет.

В общем, работать можно, но неудобно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
www.company.rt.ru
Дата регистрации
Дата основания
Численность
свыше 10 000 человек
Местоположение
Россия
Представитель
Vatuhaa