
Elasticsearch — это поисковый движок, который позволяет в реальном времени работать с огромными объемами данных. Он основан на Lucene и предлагает не только полнотекстовый поиск, но и сложные запросы к данным, включая агрегацию.
Ruby on Rails — это фреймворк, который делает акцент на скорости и простоте разработки. Используя принципы convention over configuration и DRY, Rails позволяет сосредоточиться на уникальной логике приложения, минимизируя количество шаблонного кода.
В статье рассмсотрим как использовать Elasticsearch вместе с Ruby on Rails для реализации поиска внутри приложения.
Установка и настройка
Скачаем с официального сайта Elasticsearch и следуя инструкциям установки для определенной ОС.
p.s elasticsearch требует Java
Для интеграции Elasticsearch с Rails приложением нужно добавить в Gemfile строки:
gem 'elasticsearch-model' gem 'elasticsearch-rails'
После чего юзаем команду bundle install и гемы устанавливаются в проект.
После установки гемов настраивается подключение к Elasticsearch. Это можно сделать, создав инициализатор в config/initializers с именем elasticsearch.rb и добавив в него следующий код:
Elasticsearch::Model.client = Elasticsearch::Client.new(host: 'localhost:9200')
Проверяем, что Elasticsearch запущен и доступен по указанному адресу в нашем случае localhost:9200.
Для использования Elasticsearch с моделями Rails, включаются модули Elasticsearch::Model и Elasticsearch::Model::Callbacks в модели, которые нужноиндексировать. Например:
class Article < ApplicationRecord include Elasticsearch::Model include Elasticsearch::Model::Callbacks end
Здесь автоматически синхронизируем модель с индексом Elasticsearch при создании, обновлении или удалении записей.
Основные функции
Для индексации модели в Elasticsearch нужно включить модули Elasticsearch::Model и, опционально, Elasticsearch::Model::Callbacks в модель Rails:
class Article < ApplicationRecord include Elasticsearch::Model include Elasticsearch::Model::Callbacks end
Для адекватной работы необходимо настроить маппинги — это описания того, как данные должны быть индексированы и храниться:
class Article include Elasticsearch::Model include Elasticsearch::Model::Callbacks settings index: { number_of_shards: 1 } do mappings dynamic: 'false' do indexes :title, type: 'text', analyzer: 'english' indexes :content, type: 'text', analyzer: 'english' indexes :published_at, type: 'date', format: 'strict_date_optional_time||epoch_millis' end end def as_indexed_json(options={}) as_json(only: [:title, :content, :published_at]) end end
Настраиваем индекс с одним шардом, отключаем динамическое создание маппингов и определяем маппинги для полей title, content и published_at. Также определяем метод as_indexed_json, который указывает, какие атрибуты модели должны быть сериализованы для индексации.
После настройки модели можно индексировать существующие данные, используя rake задачи или написав кастомный скрипт:
Article.find_each do |article| article.__elasticsearch__.index_document end
Код перебирает каждую статью в БД и индексирует ее в Elasticsearch.
После индексации данных можно использовать возможности поиска Elasticsearch для поиска и анализа данных:
response = Article.search('котики') response.records.each do |record| puts record.title end
Здесь делаем поиск по статьям, содержащим фразу «котики», и выводим найденных котиков.
Когда данные в модели изменяются, Elasticsearch-Model автоматически синхронизирует эти изменения с соответствующим индексом в Elasticsearch. Если нужно вручную обновить или удалить индексированные данные, можно юзать методы update_document и delete_document.
article = Article.find(1) article.title = "Updated Title" article.save # автоматически обновляет документ в Elasticsearch article.delete # автоматически удаляет документ из Elasticsearch
Прочие возможности поиска
Существует множество других вариаций реализации поиска, например можно сделать поиск по нескольким полям с разной важностью:
response = Article.search(query: { multi_match: { query: 'cats', fields: ['title^10', 'content^2', 'tags'], type: 'best_fields', tie_breaker: 0.3 } })
Запрос ищет фразу «cats» в полях title, content и tags модели Article, причем полю title придается наивысший приоритет ^10, полю content — меньший приоритет ^2, а tags используется без специфического веса. Параметр tie_breaker помогает управлять релевантностью результатов при совпадении в нескольких полях.
При определении индекса можно указать использование анализаторов, которые предварительно обрабатывают текст перед его индексацией:
response = Article.search(size: 0, aggs: { popular_tags: { terms: { field: 'tags' } } })
Для полей title и content используется анализатор my_custom_analyzer, который преобразует текст в нижний регистр и удаляет акценты
Можно использовать фаззи-поиск для исправления опечаток:
response = Article.search(query: { fuzzy: { title: { value: 'cats', fuzziness: 2 } } })
Запрос ищет слова, похожие на «cats» в поле title, допуская до двух ошибок в слове.
Предположим, у нас есть интернет-магазин, и мы хотим позволить пользователям фильтровать продукты по ценовым категориям:
response = Product.search(size: 0, aggs: { price_ranges: { range: { field: 'price', ranges: [ { to: 50 }, { from: 50, to: 100 }, { from: 100 } ] } } })
Запрос создает фасеты для продуктов в трех ценовых диапазонах: до 50, от 50 до 100, и более 100.
Elasticsearch также поддерживает геопространственные запросы, позволяя искать объекты в определенном радиусе от заданной точки
Например, можно искать все магазины в радиусе 10 километров от текущего местоположения пользователя:
response = Store.search(query: { bool: { must: { match_all: {} }, filter: { geo_distance: { distance: "10km", location: { lat: 40.715, lon: -73.988 } } } } })
Можно искать объекты, находящиеся внутри определенной географической области, заданной многоугольником:
response = Property.search(query: { geo_polygon: { location: { points: [ { lat: 40.73, lon: -74.1 }, { lat: 40.73, lon: -73.99 }, { lat: 40.74, lon: -74.1 }, { lat: 40.74, lon: -73.99 } ] } } })
Географический хэш представляет собой способ кодирования информации о местоположении в короткую строку символов, это может быть юзабельно для быстрого поиска объектов внутри определенной области.
response = Event.search(query: { geo_bounding_box: { location: { top_left: { lat: 40.73, lon: -74.1 }, bottom_right: { lat: 40.01, lon: -71.12 } } } })
Геопространственные агрегации позволяют анализировать данные на основе их местоположения, например, подсчитывая количество объектов в разных регионах:
response = Visitor.search(size: 0, aggs: { regions: { geo_hash_grid: { field: "location", precision: 3 }, aggs: { top_hits: { top_hits: { _source: { includes: [ "name", "location" ] }, size: 10 } } } } })
Elasticsearch в коннекте с Ruby on Rails, позволяют удовлетворять сложные запросы пользователей и обрабатывать большие объемы информации.
Больше практических инструментов вы сможете изучить в рамках онлайн-курсов от моих друзей из OTUS. С подробным каталогом курсов можно ознакомиться по ссылке.
ссылка на оригинал статьи https://habr.com/ru/articles/791388/
Добавить комментарий