Продолжая тему интересных возможностей грядущего релиза PostgreSQL 11, я хотел бы рассказать про новую встроенную функцию websearch_to_tsquery. Соответствующий патч разработали Виктор Дробный и Дмитрий Иванов, с правками от Федора Сигаева. Давайте же разберемся, что реализовано в этом патче.

Казалось бы, полнотекстовый поиск есть в PostgreSQL уже давно, и он очень даже неплохо работает. Что тут еще можно было добавить?

Представьте, что вы делаете интернет-магазин на базе PostgreSQL и вам нужен поиск по товарам. Вот вам прилетела форма с поисковым запросом. Для поиска по базе из этого запроса нужно как-то построить tsvector. Можно сделать это с помощью функции to_tsquery. Но to_tsquery ожидает, что строка будет в определенном формате:

=# select to_tsquery('foo bar baz');
ERROR: syntax error in tsquery: "foo bar baz"

=# select to_tsquery('foo & bar & baz');
to_tsquery
-----------------------
'foo' & 'bar' & 'baz'

Другими словами, в этом случае придется написать функцию, преобразующую запрос пользователя в запрос, понятный to_tsquery. Неудобно. Отчасти эту проблему решают функции plainto_tsquery и phraseto_tsquery:

=# select plainto_tsquery('foo bar baz');
plainto_tsquery
-----------------------
'foo' & 'bar' & 'baz'

=# select phraseto_tsquery('foo bar baz');
phraseto_tsquery
---------------------------
'foo' <-> 'bar' <-> 'baz'

Но с ними есть проблема. Дело в том, что пользователь может интуитивно воспользоваться кавычками или, скажем, какими-то булевыми операторами, потому что это работает у Google, Яндекса и других поисковых систем. Давайте посмотрим, что произойдет в этом случае:

=# select plainto_tsquery('"foo bar" -baz or qux');
plainto_tsquery
-------------------------------
'foo' & 'bar' & 'baz' & 'qux'

Все сломалось! Ой. Неужели все-таки придется писать свой парсер?

Вот чтобы его не приходилось писать с нуля для каждого приложения, начиная с PostgreSQL 11 соответствующий парсер теперь будет прямо в СУБД:

=# select websearch_to_tsquery('"foo bar" -baz or qux');
websearch_to_tsquery
----------------------------------
'foo' <-> 'bar' & !'baz' | 'qux'

Помимо того, что websearch_to_tsquery понимает кавычки, знак минус и булевы операторы, он интересен тем, что игнорирует любые попытки сделать синтаксическую ошибку. То есть, вы никогда не получите ошибку, на выходе всегда будет какой-то tsquery:

=# select websearch_to_tsquery('-"foo bar" ((( baz or or qux !@#$%^&*_+-=');
websearch_to_tsquery
--------------------------------------
!( 'foo' <-> 'bar' ) & 'baz' | 'qux'

Еще из особенностей функции следует отметить тот факт, что она игнорирует любые скобки. То есть, вот так не прокатит:

=# select websearch_to_tsquery('foo and (bar or baz)');
websearch_to_tsquery
-----------------------
'foo' & 'bar' | 'baz'

Данное поведение было выбрано из соображений, что нормальные люди (не айтишники :trollface:) на практике скобочки все равно не используют. Просто игнорируя их, мы существенно упрощаем реализацию фичи «сделать так, чтобы функция никогда не завершалась с ошибкой», да и разбор запроса будет работать быстрее. Возможно, флаг, включающий поддержку скобочек, появится в будущих версиях.

Такая вот несложная, но полезная функция. Как минимум, она делает полнотекстовый поиск в PostgreSQL не хуже, чем он был до этого, и позволяет разработчикам упростить код своих приложений.

Комментарии (9)


  1. Tantrido
    19.04.2018 17:29

    Здорово!


  1. deksden
    19.04.2018 19:22

    Здорово, конечно, но со скобочками они погорячились! Не так сложно было и сделать их.


    На моей памяти никто из обычных пользователей просто так кавычки не вставляет.


    1. SanSYS
      19.04.2018 20:32

      В результате копипасты из соседней страницы могут появиться и кавычки, и скобки


      Например в одном магазине нашёл нужный ноут, пошел искать в другом: 15.6 LED " / 3840x2160 (Матрица TFT IPS) / Intel Core i7 7820HQ


  1. SanSYS
    19.04.2018 20:39

    Круто, реально хорошо, уже сталкивался с предыдущими методами))


    Но название нового метода не самое удачное, имхо
    Десктоп разработчики просто из-за названия могут пропустить этот метод, а у них тоже бывают поля для поиска)


    auto_to_tsquery, чуть более подходит и в будущем само название нормально позволяет добавлять возможностей))


    1. rraderio
      20.04.2018 09:38

      Но название нового метода не самое удачное

      +1
      Может search_term_to_tsquery


      1. TheShock
        21.04.2018 06:58

        Или даже что-то вроде any_to_tsquery


  1. 6pirule
    19.04.2018 20:48

    Спасибо, ждал такую фичу!


  1. PaulZi
    20.04.2018 21:32

    Сделали бы shared_ispell включенным по умолчанию лучше. Вообще не понимаю зачем делать было, чтобы каждое соединение больше 500мс тратилось на считывание словарей.


  1. wikipro
    22.04.2018 12:51

    Спасибо за Вашу работу но хотелось бы чтобы


    1. была минимальная альтернатива MARIADB — Медиавики нормально с PostgreSQL не работает, форумные движки и CMS в основной массе тоже тоже, https://www.mediawiki.org/wiki/Manual:PostgreSQL Самоя простая корпоративная сеть начинается с корпоративной базы знаний на mediawiki — нельзя поставить PostgreSQL — ставим мариюДБ, раз уже использовали мариюДБ — значит и всё остальное будет на ней.
    2. Хотелось бы видеть на Вашем сайте Wiki на PostgreSQL + демоверсии основных CMS + бенчмарки. + сделайте примеры в стиле Установка Mediawiki (CMS) на LAPgP, LNgnixPgP, на ALTLinux (Для школоты и преподов информатики), AstraLinux для админов с зарплатой 20-25 т.р. госконтор. Затрат по времени для ваших инженеров будет 1-2 человеко дня, а количество установок и соответственно поток баг-отчётов возрастёт в разы.

    PS я как вы поняли несколько раз повёлся на ваши статьи и пытался использовать PostgreSQL но не получилось :( Энтузиазм ещё не угас.
    Окончательно… :).
    Удачи. Порвите ср-й MySQL с бекдорами АНБ!
    Снами Сноуден! MySQL будет разбит! Победа будет за PostgreSQL !