RailsアプリでElasticsearchを扱う
Elasticsearchとは
ElasticsearchとはElastic社が開発しているOSSの全文検索エンジンです。
大量のドキュメントから目的の単語を含むドキュメントを高速で抽出することができます。
RailsアプリでElasticsearchを扱う考え方
1.全文検索エンジンに検索対象のデータが入っている
2.アプリケーション側で検索すると、検索エンジンに対してクエリを発行し、結果が返却される
3.アプリケーション側で検索対象のデータが更新されると、連携して検索エンジンのデータも更新される
RailsアプリでElasticsearchを扱う
インデックスを作成する
Elasticsearchでは、データの保存場所としてインデックスを作成します。
リレーショナル・データベースでいうところのテーブルのようなものです。
まずは、RailsのモデルをElasticsearchでも扱うために専用のgemをインストールします。
以下をGemfileに記述し、bundle installします。
1 2 |
gem 'elasticsearch-model', github: 'elastic/elasticsearch-rails' gem 'elasticsearch-rails', github: 'elastic/elasticsearch-rails' |
bundle installが終わったら、Elasticsearchにインデックスを作成します。
検索対象としたいモデルの中で、Elasticsearch::Modelをincludeします。
1 2 3 |
class Article < ActiveRecord::Base include Elasticsearch::Model end |
これでモデルでElasticsearchを扱う準備ができました。
インデックスは以下のようなコードで作成できます。
1 |
Article.__elasticsearch__.create_index! force:true |
インデックスにドキュメントを入れる
Elasticsearchでは、インデックスに入っているデータのことをドキュメントと呼びます。
インデックスには検索対象にしたいデータを入れます。
以下のコードでElasticsearchにドキュメントをインポートします。
1 2 |
Article.import |
これでElasticsearchのインデックスの中に、ドキュメントが登録されます。
ドキュメントを検索する
ドキュメントを検索するには、Elasticsearchにクエリを投げます。
以下のように書くことで、RailsからElasticsearchにクエリを投げることができます。
1 |
response = Article.search 'hoge' |
引数で検索文字列を指定することで、ドキュメントを検索することができます。
フロントからパラメータを受け取って検索すると以下のように書けます。
1 2 3 |
def index @articles = Article.search(params) end |
Rails側で検索対象のレコードが更新されると、それに伴ってElasticsearchのドキュメントも更新する
実際にサービスを運用するとなると、Rails側でレコードが更新されるとElasticsearchのドキュメントを更新する必要があります。
まず単にElasticsearchのドキュメントを更新するには、以下のように実装します。
1 |
Article.first.__elasticsearch__.update_document |
他にも、delete_documentというメソッドがあるので、これを使えばドキュメントの削除もできます。
上記のように明示的に書かなくても、レコードを更新した際に自動的にドキュメントを更新することもできます。
gemであるelasticsearch-modelでは、Elasticsearch::Model::CallbacksをModelにincludeしておくと、レコードを更新した際にElasticsearchのドキュメントを更新するクエリを投げてくれます。
1 2 3 4 |
class Article include Elasticsearch::Model include Elasticsearch::Model::Callbacks end |
RailsアプリにElasticsearchを組み込む
実際にArticleモデルの検索周りの処理を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
require 'active_support/concern' module Article::Searchable extend ActiveSupport::Concern included do include Elasticsearch::Model index_name "article" settings index: { number_of_shards: 1, number_of_replicas: 0 } do mapping _source: { enabled: true } do indexes :id, type: 'integer', index: 'not_analyzed' indexes :title, type: 'string' indexes :content, type: 'text' end end end module ClassMethods def create_index!(options={}) client = __elasticsearch__.client client.indices.delete index: "article" rescue nil if options[:force] client.indices.create index: "article", body: { settings: settings.to_hash, mappings: mappings.to_hash } end end end |
モジュールの中で、include Elasticsearch::Modelして便利なメソッド群を使えるようにします。
index_nameはインデックス名、settingsにはインデックスの設定を書きます。
number_of_shardsやnumber_of_replicasはシャードやレプリカの設定で、耐障害性や性能に関連します。
mappingはインデックスをどのように定義するか決めます。RDBでいうテーブルスキーマのようなものです。
create_index!は実際にインデックスを作成するヘルパーです。
elasticsearch.clientでElasticsearchのクライアントのオブジェクトが取れるので、
このクライアント経由でいろいろ操作できます。
作ったモジュールをモデルにincludeします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Article < ActiveRecord::Base include Article::Searchable def self.search_message(keyword) if keyword.present? query = { "query": { "match": { "message": keyword } } } Article.__elasticsearch__.search(query) else Article.none end end end |
もらったキーワードから検索のクエリを組み立てて、Article.elasticsearch.searchに渡します。
Articleモデルに対してelasticsearch.searchを呼び出すことで、elasticsearch-railsとelasticsearch-modelがクエリを投げてくれます。
コントローラーは以下のようになります。
1 2 3 4 5 |
class ArticlesController < ApplicationController def search @keyword = params[:keyword] @articles = Article.search_message(@keyword).paginate(page: params[:page]) end |
以上がRailsアプリにElasticsearchを組み込む一例です。
最後に
実際に運用するとなると、ドキュメントの更新は非同期処理にするべきだそうです。まだまだ調べることがありそうなので、時間を作って記事にしたいと思います。
以上です!