В этой статье вы узнаете, как интегрировать ElasticSearch в приложение Rails.
Механизм полнотекстового поиска проверяет все слова в каждом сохраненном документе, пытаясь соответствовать критериям поиска (текст, указанный пользователем) википедии . Например, если вы хотите найти статьи, в которых рассказывается о Rails, вы можете искать, используя термин «rails». Если у вас нет специальной методики индексирования, это означает, что нужно полностью сканировать все записи, чтобы найти совпадения, что будет крайне неэффективно. Одним из способов решения этой проблемы является «инвертированный индекс», который отображает слова в содержании всех записей в их местоположение в базе данных.
Например, если индекс первичного ключа такой:
article#1 -> "breakthrough drug for schizophrenia"
article#2 -> "new schizophrenia drug"
article#3 -> "new approach for treatment of schizophrenia"
article#4 -> "new hopes for schizophrenia patients"
...
Инвертированный индекс для этих записей будет выглядеть так:
breakthrough -> article#1
drug -> article#1, article#2
schizophrenia -> article#1, article#2, article#3, article#4
approach -> article#3
new -> article#2, article#3, article#4
hopes -> article#4
...
Теперь при поиске слова «наркотик» используется инвертированный индекс и возвращаются статья № 1 и статья № 2 напрямую.
Я рекомендую IR Book , если вы хотите узнать больше об этом.
Создайте приложение статей
Мы начнем с известного примера блога, используемого руководствами Rails.
Создать приложение Rails
Введите в командной строке следующее:
$ rails new blog
$ cd blog
$ bundle install
$ rails s
Создайте контроллер статей
Создайте контроллер статей с помощью генератора Rails, добавьте маршруты в config / rout.rb и добавьте методы для отображения, создания и вывода статей.
$ rails g controller articles
Затем откройте config / rout.rb и добавьте этот ресурс:
Blog::Application.routes.draw do
resources :articles
end
Теперь откройте app / controllers / article_controller.rb и добавьте методы для создания, просмотра и составления списка статей.
def index
@articles = Article.all
end
def show
@article = Article.find params[:id]
end
def new
end
def create
@article = Article.new article_params
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit :title, :text
end
Модель статьи
Нам понадобится модель для статей, поэтому сгенерируйте ее так:
$ rails g model Article title:string text:text
$ rake db:migrate
Взгляды
Форма новой статьи
Создайте новый файл в app / views / article / new.html.erb со следующим содержимым:
<h1>New Article</h1>
<%= form_for :article, url: articles_path do |f| %>
<% if not @article.nil? and @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to '<- Back', articles_path %>
Показать одну статью
Создайте другой файл в app / views / article / show.html.erb :
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to '<- Back', articles_path %>
Список всех статей
Создайте третий файл в app / views / article / index.html.erb :
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<h3>
<%= article.title %>
</h3>
<p>
<%= article.text %>
</p>
</li>
<% end -%>
</ul>
<%= link_to 'New Article', new_article_path %>
Теперь вы можете добавлять и просматривать статьи. Обязательно запустите сервер Rails и перейдите по адресу http: // localhost: 3000 / article . Нажмите «Новая статья» и добавьте несколько статей. Они будут использованы для проверки наших возможностей полнотекстового поиска.
Интегрировать ElasticSearch
В настоящее время мы можем найти статью только по идентификатору . Интеграция ElasticSearch позволит найти статьи по любому слову в заголовке или тексте.
Установить для Ubuntu и Mac
Ubuntu
Перейдите наasticsearch.org/ download и загрузите файл DEB . Как только файл будет локальным, введите:
$ sudo dpkg -i elasticsearch-[version].deb
макинтош
Если вы работаете на Mac, Homebrew делает это легко:
$ brew install elasticsearch
Подтвердите установку
Откройте этот URL: http: // localhost: 9200, и вы увидите, что ElasticSearch отвечает так:
{
"status" : 200,
"name" : "Anvil",
"version" : {
"number" : "1.2.1",
"build_hash" : "6c95b759f9e7ef0f8e17f77d850da43ce8a4b364",
"build_timestamp" : "2014-06-03T15:02:52Z",
"build_snapshot" : false,
"lucene_version" : "4.8"
},
"tagline" : "You Know, for Search"
}
Добавить базовый поиск
Создайте контроллер с именем search
/ search? Q = ruby .
Gemfile
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
Не забудьте запустить bundle install
Контроллер поиска
Создайте SearchController
$ rails g controller search
Добавьте этот метод в app / controller / search_controller.rb :
def search
if params[:q].nil?
@articles = []
else
@articles = Article.search params[:q]
end
end
Интегрируйте поиск в статью
Чтобы добавить интеграцию ElasticSearch к модели Article, потребуйте эластичный поиск elasticsearch/model
Article
Изменить приложение / models / article.rb :
require 'elasticsearch/model'
class Article < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
end
Article.import # for auto sync model with elastic search
Поиск в представлении
Создайте новый файл в app / views / search / search.html.erb :
<h1>Articles Search</h1>
<%= form_for search_path, method: :get do |f| %>
<p>
<%= f.label "Search for" %>
<%= text_field_tag :q, params[:q] %>
<%= submit_tag "Go", name: nil %>
</p>
<% end %>
<ul>
<% @articles.each do |article| %>
<li>
<h3>
<%= link_to article.title, controller: "articles", action: "show", id: article._id%>
</h3>
</li>
<% end %>
</ul>
Маршрут поиска
Добавьте поисковый маршрут к _config / rout.rb -:
get 'search', to: 'search#search'
Теперь вы можете перейти на http: // localhost: 3000 / search и найти любое слово в созданных вами статьях.
Улучшить поиск
Вы можете заметить, что в вашей поисковой системе есть некоторые ограничения. Например, поиск части слова, например «rub» или «roby» вместо «ruby» , даст нулевой результат. Кроме того, было бы неплохо, если бы поисковая система давала результаты, содержащие слова, похожие на ваш поисковый запрос.
ElasticSearch предоставляет множество функций для улучшения поиска. Я приведу несколько примеров.
Пользовательский запрос
Существуют разные типы запросов, которые мы можем использовать. Пока что мы просто используем запрос ElasticSearch по умолчанию. Чтобы улучшить результаты поиска, нам нужно изменить этот запрос по умолчанию. Мы можем, например, дать более высокий приоритет для таких полей, как заголовок над другими полями.
ElasticSearch предоставляет полный Query DSL на основе JSON для определения запросов. В общем, есть основные запросы, такие как термин или префикс. Есть также составные запросы, такие как запрос bool. С запросами также могут быть связаны фильтры, например фильтрованные запросы или запросы constant_score.
Давайте добавим собственный метод поиска к нашей модели статьи в app / models / article.rb :
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title^10', 'text']
}
}
}
)
end
Примечание. ^ 10 увеличивает на 10 баллов количество совпадений, когда искомое условие соответствует названию.
Custom Mapping
Сопоставление — это процесс определения того, как документ должен быть сопоставлен с поисковой системой, включая его характеристики для поиска, например, какие поля доступны для поиска и если / как они маркированы.
Явное отображение определяется на уровне индекса / типа. По умолчанию нет необходимости определять явное сопоставление, поскольку оно автоматически создается и регистрируется при вводе нового типа или нового поля (без снижения производительности) и имеет разумные значения по умолчанию. Только когда необходимо переопределить значения по умолчанию, должно быть предоставлено определение отображения.
Мы улучшим поиск, чтобы вы могли искать такой термин, как «поиск», и получать результаты, в том числе «поиски» и «поиск» .. и т. Д. Это позволит использовать встроенный анализатор английского языка в ElasticSearch, чтобы применить определение слова перед индексацией.
Добавьте это отображение в класс Article: app / models / article.rb
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :title, analyzer: 'english'
indexes :text, analyzer: 'english'
end
end
Рекомендуется добавить следующие строки в конец файла, чтобы автоматически удалять и перестраивать индекс при загрузке article.rb:
# Delete the previous articles index in Elasticsearch
Article.__elasticsearch__.client.indices.delete index: Article.index_name rescue nil
# Create the new index with the new mapping
Article.__elasticsearch__.client.indices.create \
index: Article.index_name,
body: { settings: Article.settings.to_hash, mappings: Article.mappings.to_hash }
# Index all article records from the DB to Elasticsearch
Article.import
Подсветка поиска
По сути, мы хотели бы показать те части статей, где появляется искомый термин. Это похоже на поиск в Google, и вы видите образец документа, в котором ваш термин выделен жирным шрифтом. В ElasticSearch это называется «основные моменты». Мы добавим параметр подсветки в наш запрос и укажем поля, которые мы хотим выделить. ElasticSearch вернет термин между тег, а также несколько слов до и после термина.
Предполагая, что мы ищем термин «с открытым исходным кодом» , ElasticSearch вернет что-то вроде этого:
Elasticsearch is a flexible and powerful <em>opensource</em>, distributed, real-time search and analytics
Обратите внимание, что «opensource» окружен тег.
Добавить основные моменты в SearchController
Сначала добавьте параметр «highlight» в запрос ElasticSearch:
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title^10', 'text']
}
},
highlight: {
pre_tags: ['<em>'],
post_tags: ['</em>'],
fields: {
title: {},
text: {}
}
}
}
)
end
Показать основные моменты в представлении
Это довольно легко показать эту изюминку в представлении. Перейдите в app / views / search / search.html.erb и замените элемент ul
<ul>
<% @articles.each do |article| %>
<li>
<h3>
<%= link_to article.try(:highlight).try(:title) ? article.highlight.title[0].html_safe : article.title,
controller: "articles",
action: "show",
id: article._id%>
</h3>
<% if article.try(:highlight).try(:text) %>
<% article.highlight.text.each do |snippet| %>
<p><%= snippet.html_safe %>...</p>
<% end %>
<% end %>
</li>
<% end %>
</ul>
Теперь добавьте стиль для в приложении / assets / stylesheets / search.css.scss :
em {
background: yellow;
}
И последнее, что нам нужно, это выделенный термин, возвращаемый ElasticSearch, который будет заключен в несколько слов. Если вам нужно показать заголовок с начала, добавьте index_options: 'offsets'
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :title, analyzer: 'english', index_options: 'offsets'
indexes :text, analyzer: 'english'
end
end
Это был быстрый пример интеграции ElasticSearch в приложение Rails. Мы добавили базовый поиск, а затем немного перемешали, используя пользовательские запросы, сопоставления и выделения. Вы можете скачать полный источник здесь