Как внедрить поиск в реальный проект всего за 10 минут

Введение

На часах было почти 3 часа дня, я хотел было сделать себе послеобеденный кофе, но получил новое задание от тимлида. Необходимо было сделать поиск для онлайн платформы просмотра ТВ, сериалов и фильмов.

Старт

Я приступил к выполнению задачи. Запросы в гугле были разного уровня: от “зачем нужен поиск”, до “имплементация обратного индекса в СУБД”. Вот что удалось найти:

  • “Серьезные пацаны” используют для поиска всякие Sphinx и ElasticSearch. Минусы: их нужно настраивать, загонять в них данные и мониторить их потребности.
  • Haystack обеспечивает модульный поиск Django. У него простой и понятный API для работы над поиском, который позволяет подключать различные поисковые движки. Однако выявилась проблема в настройке минимального количества символов в системе серча: при значении — 1 он переставал корректно работать.
  • Любая современная база данных имеет в себе встроенный полнотекстовый поиск.

После прочтения “кучи” статей о настройках поиска в хорошем виде в django, я остановился на PostgreSql, так как у “постгреса” часто появляются новые “плюшки”.

Подготовка окружения

Установка и настройка PostgreSql и Django настолько простая в нашем случае, что вы можете ее сделать по гайду. Также прочтите немного теории.

Индексирование в Django проекте

Индексирование бывает прямым и обратным. Например, если с помощью индексирования мы узнаём, что в левой части супермаркета есть баклажаны, картофель, помидоры и мармелад; в правой — мороженое, сухофрукты, мармелад и хлеб, то это прямой индекс.

Но что если нам нужно найти места, где есть мармелад? Придется пробегать циклом по всем местам и выбирать те, в которых имеется неободимый продукт.

Есть и другое решение. Продолжив читать книгу “Поиск. Индексы. И так далее” мы увидим, что нам может помочь обратный индекс. Он работает противоположно прямому, и после применения можно увидеть такую структуру:

Помидоры — левая сторона, при входе

Картофель — конец, левая сторона

Мармелад — левая сторона, правая сторона

Теперь мы точно знаем, где есть наш мармелад!

Возникает новый вопрос, как безболезненно внедрить эту “фичу” в наш Django проект?

Нашел решение. Благодаря разработчикам “постгреса”, сегодня есть простой в использовании GIN, который работает на основе обратного индекса.

Для использования его в “джанге” нужно всего лишь поставить psycorg и импортировать необходимые зависимости.

Внедрение Gin индекса в существующее поле модели Movies

Тем самым мы проиндексировали поле title. Теперь давайте посмотрим, создался ли индекс внутри базы данных. Проверить можно командой:

SELECT tablename, indexname, tablespace, indexdef  FROM pg_indexes WHERE tablename = ‘ваша таблица’;

Мы видим, что буквально с помощью одной строки кода мы упростили жизнь. И так, у нас есть проиндексированные данные, но как с ними работать? Будет ли работать ‘__icontains’, если просто указать необходимое поле в запросе?

Ответ: Да

Давайте разберем детально, почему.

Работа с запросами

Выше мы уже определили модель Movies, давайте продолжим работать с ней же.

Посмотрите, как выглядит обычный запрос Django ORM и Sql.

Django

обычный запрос, для поиска записей имеющих внутри себя <query>, используя filter.

Sql

SELECT vod_movies.title, vod_movies.release_dateFROM vod_moviesWHERE vod_movies.title like ‘%Kings%’;

Вывод

<QuerySet [<Movies: Kingsman: The Golden Circle>]>

Чтобы выполнить более сложный запрос, мы должны использовать три новых класса: SearchVector, SearchQuery, SearchRank.

SearchVector

Можно использовать SearchVector для создания нашего запроса с использованием нескольких полей. Затем использовать <filter>.

Django

запрос на поиск значения “Wilson” в полях title и summary.

Sql

SELECT “vod_movies”.”title”, “vod_movies”.”release_date”, “vod_movies”.”summary”to_tsvector(COALESCE(“vod_movies”.”title”, ) || ‘ ‘ || COALESCE(“vod_movies”.”summary”, ))AS “search”FROM “vod_movies”WHERE to_tsvector(COALESCE(“vod_movies”.”title”, ) || ‘ ‘ ||COALESCE(“vod_movies”.”summary”, )) @@ (plainto_tsquery(Wilson)) = true

Вывод

<QuerySet [<Movies: Wilson>, <Movies: The Pursuit of Happyness>]>

Так мы нашли фильмы “Wilson” и “The Pursuit of Happiness”. В первом случае, слово “Wilson” содержится в названии, во втором, в описании картины (слово “Wilson” в описании ленты добавил лично, потому что было лень искать слово, которое будет удовлетворять запросу).

SearchQuery

Бывает и такое, когда нужно сделать поиск по нескольким словам в базе данных. Представьте, вы ищете фильм в описании которого содержится фраза “good life”. SearchQuery позволит задать порядок этих слов в описании.

Django

Запрос на поиск фильма по фразе в полях title и summary

Sql

SELECT “vod_movies”.”title”, “vod_movies”.”release_date”to_tsvector(COALESCE(“vod_movies”.”title”, ) || ‘ ‘ || COALESCE(“vod_movies”.”summary”, ))AS “search”FROM “vod_movies”WHERE to_tsvector(COALESCE(“vod_movies”.”title”, ) || ‘ ‘ ||COALESCE(“vod_movies”.”summary”, )) @@ ((plainto_tsquery(good) && plainto_tsquery(life))) = true

Вывод:

<QuerySet [<Movies: Locke>]>
Описание фильма “Locke” на сайте

SearchRank

Думаю, лучшее в данной статье — это SearchRank, так как он дает возможность посмотреть, насколько каждый элемент удовлетворяет запросу.

Только представьте, что вы хотите найти фильм, но не помните его названия. Все что вы помните — это то, что в названии были слова “Girl” и “Tattoo”. Могу Вас обрадовать SearchRank отлично справится с задачей: найдёт и отсортирует по степени “схожести” результат.

Django

запрос на поиск фильма по ключевым словам в поле title.

Sql

SELECT “vod_movies”.”title”,ts_rank(to_tsvector(COALESCE(“vod_movies”.”title”, )), (plainto_tsquery(Girl) || plainto_tsquery(Tattoo)))AS “rank”FROM “vod_movies” ORDER BY “rank” DESC

Вывод

<QuerySet [(u’The Girl with the Dragon Tattoo’, 0.0607927), (u’Inside Girl’, 0.0303964), (u’Kingsman: The Golden Circle’, 0.0)]
отображение результата поиска по ключевым словам на сайте

Заключение

Люди пользуются поиском везде, поэтому он неотъемлемая часть большинства проектов. Поиск, который я реализовал, внедрен на сайт, где количество фильмов и сериалов превышает несколько тысяч.

Более того, при тщательной настройке PostgreSql с Django можно получить реактивный поиск. Надеюсь, что после прочтения статьи вы захотите поработать с Postgresql.

Хочешь узнать больше о Mad Devs?

--

--