はじめに
elasticsearch-railsを使うことが前提の記事になります。
記事の中で出てくるサンプルや環境は、RailsとElasticsearchで検索機能をつくり色々試してみる - その1:サンプルアプリケーションの作成をもとにしています。
やりたいこと
今回はSynonym(類義語)を扱っていきます。
Synonym(類義語)を登録しておくことで、例えば「ひろあか」と入力して「ヒーローアカデミア」を検索できるようにしていきます。
例)
「ひろあか」 → 「ヒーローアカデミア」
「てんすら」 → 「スライム」
「すらだん」 → 「スラムダンク」
Synonymを登録する方法
ElasticsearchでSynonymを登録する方法は大きくは2種類あります。
- Synonymを定義したファイルを作成して、ファイルのパスをフィルターに定義する
- フィルターに直接Synonymの定義を登録する
これら二つの方法で登録する方法をサンプルコードを使って紹介していきます。
修正前の状態のコードをのせておきます。 修正前のコード
カスタムAnalyzerの登録
Synonymの登録ではAnalyzerにFilterを登録していくことになります。そのためビルドインのkuromoji
ではなく独自に定義したAnalyzerを定義し、そのAnalyzerにFilterを追加していきます。
まずは検索処理用のConcernにAnalyzerを定義します。
module MangaSearchable
extend ActiveSupport::Concern
included do
include Elasticsearch::Model
index_name "es_manga_#{Rails.env}"
+ settings analysis: self.analyzer_settings do # 追加したanalyzer_settingsメソッドでAnalyzerを登録する
mappings dynamic: 'false' do
indexes :id, type: 'integer'
indexes :publisher, type: 'keyword'
indexes :author, type: 'keyword'
indexes :category, type: 'text', analyzer: 'kuromoji'
+ indexes :title, type: 'text', analyzer: 'custom_kuromoji' # 追加したAnalyzerを使う
+ indexes :description, type: 'text', analyzer: 'custom_kuromoji' # 追加したAnalyzerを使う
end
end
・
・
・
end
・
・
・
class_methods do
・
・
・
# Synonymを追加していくためのAnalyzerを定義。中身はビルドインのkuromojiをベースにしています
+ def analyzer_settings
+ {
+ analyzer: {
+ custom_kuromoji: {
+ type: 'custom',
+ char_filter: [],
+ tokenizer: "kuromoji_tokenizer",
+ filter: [
+ 'kuromoji_baseform', 'kuromoji_part_of_speech',
+ 'cjk_width', 'kuromoji_stemmer', 'lowercase'
+ ]
+ }
+ },
+ }
+ end
end
end
ファイルを使ったSynonymの登録
準備ができたのでここからSynonymの話です。まずはファイルを使った登録を行っていきます。
ひろあか,ヒロアカ => ヒーローアカデミア
てんすら,テンスラ => スライム
今回は=>
を使った左側の単語を右側の単語に置き換える書き方にしていますが、ひろあか,ヒロアカ,ヒーローアカデミア
のようにカンマ区切りでグルーピングする方法もあります。
ファイルができたら、Elasticsearchが読み込めるようにファイルを配備して、さきほどのAnalyzerを修正していきます。
ファイルの配備
さきほど作成したファイルをコンテナ内にコピーします。
$ docker cp files/synonym.txt elasticsearch_sample:/usr/share/elasticsearch/config/synonym.txt
# コピーしたファイルは必要に応じてElasticsearchから参照できるようにオーナーやパーミションを修正
Analyzerの修正
さきほど配備したsynonym.txtを使ったfilter(manga_synonym)を定義して、それをAnalyzer(custom_kuromoji)のfilterに追加します。
module MangaSearchable
・
・
・
def analyzer_settings
{
analyzer: {
custom_kuromoji: {
type: 'custom',
char_filter: [],
tokenizer: "kuromoji_tokenizer",
filter: [
+ 'manga_synonym', # 追加したfilterを先頭に追加
'kuromoji_baseform', 'kuromoji_part_of_speech',
'cjk_width', 'kuromoji_stemmer', 'lowercase'
]
}
},
+ filter: { # synonym用のfilterを追加
+ manga_synonym: {
+ type: 'synonym',
+ synonyms_path: 'synonym.txt',
+ }
+ }
}
end
end
end
定義が完了したのでindexを再作成、データを入れ直します。
$ docker exec -it rails_es_rails_1 /bin/bash
/app# bundle exec rake elasticsearch:create_index
/app# bundle exec rake elasticsearch:import_manga_all
これで「ひろあか」で検索すると「ヒーローアカデミア」で検索した結果と同じ結果が得られると思います。
フィルターに直接Synonymを定義
次にフィルターに直接定義する方法を見ていきます。
Analyzerの修正
filter(manga_synonym_2)を定義し、その中で直接Synonymを定義します。定義したfilterをAnalyzer(custom_kuromoji)のfilterに追加します。
module MangaSearchable
・
・
・
def analyzer_settings
{
analyzer: {
custom_kuromoji: {
type: 'custom',
char_filter: [],
tokenizer: "kuromoji_tokenizer",
filter: [
'manga_synonym',
+ 'manga_synonym_2', # 新たにfilterを追加
'kuromoji_baseform', 'kuromoji_part_of_speech',
'cjk_width', 'kuromoji_stemmer', 'lowercase'
]
}
},
filter: {
manga_synonym: {
type: 'synonym',
synonyms_path: 'synonym.txt',
},
+ manga_synonym_2: { # filterを追加
+ type: 'synonym',
+ synonyms: [ # synonymsに直接登録していく
+ 'すらだん => スラムダンク',
+ ]
+ },
}
}
end
end
end
定義が完了したのでindexを再作成、データを入れ直します。
$ docker exec -it rails_es_rails_1 /bin/bash
/app# bundle exec rake elasticsearch:create_index
/app# bundle exec rake elasticsearch:import_manga_all
これで「すらだん」で検索すると「スラムダンク」で検索した結果と同じ結果が得られると思います。
まとめ
elasticsearch-railsを使ってRailsでSynonymを使う方法を紹介しました。(今回の修正内容になります)
どちらの方法もわりと簡単に登録できて便利ですね。単語の数が増えてくるとメンテナンスの方法などは工夫が必要になってくると思いますが、検索の精度を上げるためにも積極的に使っていきたいところです。
参考
Custom Analyzer | Elasticsearch Reference [7.3] | Elastic
Amazon Elasticsearch Service で類義語(Synonym)を扱う | DevelopersIO