Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails
Code

Полнотекстовый поиск в Rails с использованием Elasticsearch

by
Length:MediumLanguages:

Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)

В этой статье я расскажу вам, как реализовать полнотекстовый поиск с использованием Ruby on Rails и Elasticsearch. В настоящее когда вы вводите поисковую фразу, то вы сразу же получаете как результаты поиска, так и предложения по поиску. Если вы опечатались при поиске, то автоматическая корректировка также является хорошей функцией, как мы можем видеть на таких сайтах, как Google или Facebook.

Для реализации всех этих функций использование только реляционной базы данных, такой как MySQL или Postgres, не достаточно. По этой причине мы используем Elasticsearch, который вы можете рассматривать как базу данных, специально созданную и оптимизированную для поиска. Это решение с открытым исходным кодом, и оно построено поверх Apache Lucene.

Одна из самых приятных особенностей Elasticsearch заключается в том, что она предоставляет свои функции с использованием REST API, поэтому есть библиотеки, которые обрачивают эту функциональность для большинства языков программирования.

Представляем Elasticsearch

Ранее я упомянул, что Elasticsearch похож на базу данных для поиска. Было бы полезно, если бы вы были знакомы с некоторой терминологией вокруг него.

  • Field: Поле похоже на пару «ключ-значение». Значение может быть простым значением (string, integer, date) или вложенной структурой, например массивом или объектом. Поле похоже на столбец таблицы в реляционной базе данных.
  • Document: документ представляет собой список полей. Это документ JSON, который хранится в Elasticsearch. Это похоже на строку в таблице в реляционной базе данных. Каждый документ хранится в индексе и имеет тип и уникальный идентификатор.
  • Type: Тип подобен таблице в реляционной базе данных. Каждый тип имеет список полей, которые могут быть указаны для документов этого типа.
  • Index: индекс является эквивалентом реляционной базы данных. Он содержит определение для нескольких типов и хранит несколько документов.

Здесь следует отметить, что в Elasticsearch, когда вы пишете документ в индекс, поля документа анализируются по слову, чтобы сделать поиск простым и быстрым. Elasticsearch также поддерживает геолокацию, поэтому вы можете искать документы, расположенные на определенном расстоянии от определенного места. Именно так Foursquare реализует поиск.

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

Установка Elasticsearch

Если вы используете Linux, возможно, вы можете установить Elasticsearch из одного из репозиториев. Он доступен в APT и YUM.

Если вы используете Mac, вы можете установить его с помощью Homebrew: brew install elasticsearch. После установки elasticsearch вы увидите список соответствующих папок в вашем терминале:

Elasticsearch folders

Чтобы убедиться, что установка работает, введите текст elasticsearch в своем терминале, чтобы запустить его. Затем выполните curl localhost: 9200 в вашем терминале, и вы увидите что-то вроде:

Elasticsearch running

Установка Elastic HQ

Elastic HQ - это плагин мониторинга, который мы можем использовать для управления Elasticsearch в браузере, подобно phpMyAdmin для MySQL. Чтобы установить его, просто запустите в своем терминале:

/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso / elasticsearch-HQ

После его установки перейдите в http://localhost:9200/_plugin/hq в своем браузере:

Elastic HQ Plugin

Нажмите Connect, и вы увидите экран, показывающий состояние кластера:

Elastic HQ Cluster Overview

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

Создание приложения Rails

Я собираюсь создать очень простое приложение Rails, где вы можете добавлять статьи в базу данных, чтобы мы могли выполнять полнотекстовый поиск по ним с помощью Elasticsearch. Начните с создания нового приложения Rails:

rails new elasticsearch-rails

Затем мы создаем новый ресурс статьи:

rails generate scaffold Article title:string text:text

Теперь нам нужно добавить новый корневой маршрут, поэтому мы можем по умолчанию просмотреть список статей. Изменим config/routes.rb:

Создайте базу данных, выполнив команду rake db: migrate. Если вы запустите rails server, откройте свой браузер, перейдите на localhost:3000 и добавьте несколько статей в базу данных или просто загрузите файл db/seeds.rb с фиктивными данными, которые я создал, поэтому вам не нужно тратить много времени на заполнение форм.

Добавление поиска

Теперь, когда у нас есть небольшое приложение Rails со статьями в базе данных, мы готовы добавить наши функции поиска. Мы собираемся начать с добавления ссылки на официальные Elasticsearch Gems:

На многих веб-сайтах очень часто есть текстовое поле для поиска в верхнем меню на всех страницах. По этой причине я собираюсь создать форму в app/views/search/_form.html.erb. Как вы можете видеть, я отправляю форму с помощью GET, поэтому легко скопировать и вставить URL для определенного поиска.

Добавьте ссылку на форму на основной макет сайта. Изменим app/views/layouts/application.html.erb.

Теперь нам также нужен контроллер для выполнения фактического поиска и отображения результатов, поэтому мы создаем его, запуская команду rails g new controller Search.

Как вы можете видеть, я вызываю метод search в модели Article. Мы еще не определили его, поэтому, если мы попытаемся выполнить поиск в этот момент, мы получим ошибку. Кроме того, мы не добавили маршрут для SearchController в файле config/routes.rb, поэтому давайте сделаем это:

Если мы посмотрим на документацию для gem elasticsearch-rails, нам нужно включить два модуля в модели, которые мы хотим проиндексировать в Elasticsearch, в нашем случае Article.rb.

Первая модель вводит метод поиска, который мы использовали в нашем предыдущем контроллере среди других. Второй модуль интегрируется с обратными вызовами ActiveRecord для индексации каждого экземпляра статьи, которую мы сохраняем в базе данных, а также обновляет индекс, если мы модифицируем или удаляем статью из базы данных. Так что это все прозрачно для нас.

Если вы ранее импортировали данные в базу данных, эти статьи по-прежнему не входят в индекс Elasticsearch; только новые индексируются автоматически. По этой причине мы должны индексировать их вручную, и это легко сделать запустив rails console. Затем нам нужно только запустить irb (main)> Article.import.

Articleimport

Теперь мы готовы попробовать функцию поиска. Если я наберу «ruby» и нажму Поиск, то получу такие результаты:

Search Result

Подсветка поиска

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

Измените app/models/article.rb и измените метод поиска по умолчанию:

По умолчанию метод search определяется моделями «elasticsearch-models», а прокси-объект __elasticsearch__ предоставляется для доступа к классу-оболочке для API Elasticsearch. Таким образом, мы можем изменить запрос по умолчанию, используя стандартные параметры JSON, как указано в документации.

Теперь метод поиска будет обертывать результаты, соответствующие запросу с указанными тегами HTML. По этой причине нам также необходимо обновить страницу результатов поиска, чтобы мы могли безопасно отображать HTML-теги. Для этого отредактируйте app/views/search/search.html.erb.

Добавьте стиль CSS в app/assets/stylesheets/search.scss для нужного тега:

Попробуйте снова поискать «ruby»:

Search result

Как вы можете видеть, легко выделить термин поиска, но не идеально, поскольку нам нужно отправить запрос JSON, как указано в документации Elasticsearch, и у нас нет какой-либо абстракции.

Searchkick Gem

Searchkick gem  предоставляется Instacart, и это абстракция поверх официальных gem-ов Elasticsearch. Я собираюсь реорганизовать выделенную функциональность, поэтому мы начинаем с добавления gem 'searchkick' в gemfile. Первым классом, который нам нужно изменить, является модель Article.rb:

Как вы можете видеть, это намного проще. Нам нужно снова переопределить статьи и выполнить команду rake searchkick: reindex CLASS = Article. Чтобы выделить поисковый запрос, нам нужно передать дополнительный параметр методу поиска из нашего search_controller.rb.

Последним файлом, который нам нужно изменить, является view/search/search.html.erb, поскольку результаты возвращаются в другом формате с помощью поиска:

Теперь пришло время снова запустить приложение и протестировать функцию поиска:

Search result

Обратите внимание, что я ввел поисковый запрос «dato». Я сделал это специально, чтобы показать вам, что по умолчанию searchkick настроен для анализа проиндексированного текста.

Автоматические подсказки

Autosuggest или typeahead предсказывает, что пользователь вводит, делая поиск быстрее и проще. Имейте в виду, что, если у вас нет тысяч записей, лучше всего фильтровать на стороне клиента.

Начнем с добавления плагина typeahead, который доступен через gem 'bootstrap-typeahead-rails' и добавьте его в свой Gemfile. Затем нам нужно добавить JavaScript в app/assets/javascripts/application.js, чтобы при вводе текста в поле поиска появилось несколько предложений.

Несколько комментариев о предыдущем фрагменте. В последних двух строках, поскольку я не отключил turbolinks, это способ подключить код, который я хочу запустить при загрузке страницы. В первой части скрипта вы можете видеть, что я использую Bloodhound. Это движок typeahead.js, и я также настраиваю конечную точку JSON, чтобы сделать запросы AJAX, чтобы получить предложения. После этого я вызываю initialize() в движке, и я настраиваю typeahead в текстовом поле поиска, используя свой идентификатор «term».

Теперь нам нужно выполнить бэкэнд-реализацию для предложений, давайте начнем с добавления маршрута, отредактируйте app/config/routes.rb.

Затем я добавлю реализацию на app/controller/search_controller.rb.

Этот метод возвращает результаты поиска для введенного термина с использованием JSON. Я только ищу по названию, но я мог бы указать тело статьи тоже. Я также ограничиваю количество результатов поиска до 10 максимум.

Теперь мы готовы попробовать реализацию typeahead:

Typeahead

Заключение

Как вы можете видеть, использование Elasticsearch с Rails делает поиск наших данных очень простым и быстрым. Здесь я показал вам, как использовать gem-ы низкого уровня, предоставляемые Elasticsearch, а также gem Searchkick, который представляет собой абстракцию, которая скрывает некоторые детали того, как работает Elasticsearch.

В зависимости от ваших конкретных потребностей вы можете использовать Searchkick и быстро и легко выполнять полнотекстовый поиск. С другой стороны, если у вас есть другие сложные запросы, в том числе фильтры или группы, вам может потребоваться больше узнать о деталях языка запросов в Elasticsearch и в конечном итоге использовать gem-ы более низкого уровня, такие как  'elasticsearch-models' и 'elasticsearch-rails'.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.