TL;DR
漢字で保持しているユーザの名前をひらがな、カタカナでも検索できるようにします。
使ってみる
以下のURLにアクセスして、上部のサーチボックスから検索が行えます。
ログインするとよりたくさんの結果が返ります。お試しください。
環境
- Elasticsearch v1.2.1
- kuromoji for Elasticsearch v2.2.0
方針
-
kuromoji_tokenizer
で形態素解析した単語の漢字部分をkuromoji_readingform
(TokenFilter)でカタカナに変換してインデックスします。 - 変換のために別途人名辞書を作成し
kuromoji_tokenizer
のユーザ辞書に登録して漢字とカタカナの紐付けを行います。 - ユーザ辞書はトークンと読みがなを1対1でしか紐づけできないので、
word_delimiter
(TokenFilter)を活用して1対Nで紐づけできるようにします。
人名辞書(ユーザ辞書)
ユーザ辞書の仕様
カンマ区切りのCSV形式ファイルで文字コードはUTF8
です。
1つのトークンに対して、1つの読みがなを紐付けることができます。
仕様
<text>,<token 1> ... <token n>,<reading 1> ... <reading n>,<part-of-speech tag>
例 東京 (トークンが1つ)
東京,東京,トウキョウ,カスタム名詞
例 東京スカイツリー (トークンが2つ)
東京スカイツリーは、トークン東京
, スカイツリー
にわけられて、東京
トークンの読みがなはトウキョウ
と定義しています。
東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞
ファイル名は任意になります。
例
person.dic
辞書ファイルの配置場所
OSX
elasticsearchはbrew
でインストールしました。
以下の1.2.1
の部分は任意のバージョンになります。
/usr/local/Cellar/elasticsearch/1.2.1/config/person.dic
Ubuntu
elasticsearchはapt
でインストールしました。
/usr/local/etc/elasticsearch/person.dic
Other Linux
/etc/elasticsearch/person.dic
人名辞書ファイルの作成
ファイル名をここではperson.dic
としました。
イマイ
⇒ 今井
と読みがなを紐付けたい場合は以下のようになります。
$ ack '^今井' person.dic
今井,今井,イマイ,人名固有名詞
人名漢字に多様な読みがなを紐付ける
Kuromojiのユーザ辞書の仕様はトークンと読みがなが1対1で対応します。
イマイ
⇒ 今井
は記述できましたが、カワサキ
カワザキ
⇒ 川崎
は記述できません。
以下のように記述すると辞書をロードしたときにエラーになります。
$ ack '^川崎' person.dic
川崎,川崎,カワサキ カワザキ,人名固有名詞
$ ack '^川崎' person.dic
川崎,川崎 川崎,カワサキ カワザキ,人名固有名詞
人名の漢字は多様な読み方をする場合が多いので対応は必須です。
以下の方法にて対応しました。
読みがなを任意のデミリター区切りで記述しました。
ここでは%
にしました。
$ ack '^川崎' person.dic
川崎,川崎,カワサキ%カワザキ,人名固有名詞
$ ack '^山田' person.dic
山田,山田,ヤダ%ヤマタ%ヤマダ%ヨウダ%ヨマダ,人名固有名詞
$ ack '^井上' person.dic
井上,井上,イウエ%イカミ%イガミ%イナイ%イナエ%イネ%イネイ%イノウエ%イノエ,人名固有名詞
このままでは正しく検索できないので、word_delimiter
(TokenFilter)でデミリター区切りを分割して、個々の読みがなとして切り出します。( 詳細は後述 )
Index setting
{
index: my_index,
body: {
settings: {
analysis: {
tokenizer: {
# kuromojiにユーザ辞書を設定したトークナイザー
kuromoji_pserson_dic: { type: 'kuromoji_tokenizer', user_dictionary: 'person.dic' }
},
filter: {
# 読みがな変換用のフィルター
katakana_readingform: {
type: 'kuromoji_readingform',
use_romaji: false
},
# デミリター区切りを分割するフィルター
split_delimiter: {
type: 'word_delimiter',
generate_word_parts: true,
generate_number_parts: false,
catenate_words: false,
catenate_numbers: false,
catenate_all: false,
split_on_case_change: false,
preserve_original: false,
split_on_numerics: false,
stem_english_possessive: false
}
},
analyzer: {
# ユーザ辞書トークナイザーに読みがな変換用のフィルター、デミリター区切りを分割するフィルターを
# 設定したアナライザー
yomigana_analyzer: {
type: 'custom',
tokenizer: 'kuromoji_pserson_dic',
filter: ['katakana_readingform', 'split_delimiter']
}
}
}
}
}
}
yomigana_analyzer
下記アナライザーにより後述のようにインデックスが行えるようになります。
# ユーザ辞書トークナイザーに読みがな変換用のフィルター、デミリター区切りを分割するフィルターを
# 設定したアナライザー
yomigana_analyzer: {
type: 'custom',
tokenizer: 'kuromoji_pserson_dic',
filter: ['katakana_readingform', 'split_delimiter']
}
例 山田 花子
<INPUT>
山田 花子
↓
<kuromoji_tokenizer>
山田
, 花子
↓
<katakana_readingform>
ヤダ%ヤマタ%ヤマダ
, ハナコ%カコ
↓
<split_delimiter>
ヤダ
, ヤマタ
, ヤマダ
, ハナコ
, カコ
split_delimiter
word_delimiter
は、文字列をオプションにより多様な方法で分割するフィルターです。
オプション
generate_word_parts
単語トークンを生成 "PowerShot" ⇒ "Power" "Shot"
generate_number_parts
数字トークンを生成 "500-42" ⇒ "500" "42"
catenate_words
英字を連結したトークンを生成 "wi-fi" ⇒ "wifi"
catenate_numbers
数値を連結したトークンを生成 "500-42" ⇒ "50042"
catenate_all
単語を連結したトークンを生成 "wi-fi-4000" ⇒ "wifi4000"
split_on_case_change
ケースセンシティブが出現した箇所で分割 "PowerShot" ⇒ "Power" "Shot"
preserve_original
オリジナルの文字列を含んでトークンを生成 "500-42" ⇒ "500-42" "500" "42"
split_on_numerics
数字が出現した箇所で分割 "j2se" ⇒ "j" "2" "se"
stem_english_possessive
末尾のs
"O’Neil’s" ⇒ "O", "Neil"
デフォルトでtrue
になっている項目もあるので、使わないものは明示的にfalse
にしています。
refs word delimiter token filter
# デミリター区切りを分割するフィルター
split_delimiter: {
type: 'word_delimiter',
generate_word_parts: true,
generate_number_parts: false,
catenate_words: false,
catenate_numbers: false,
catenate_all: false,
split_on_case_change: false,
preserve_original: false,
split_on_numerics: false,
stem_english_possessive: false
}
Document mapping
{
index: my_index,
type: 'user',
body: {
user: {
_id: { path: 'id' },
_timestamp: { enabled: true },
_source: { enabled: true },
properties: {
# 漢字の名前のアナライザーとして、yomigana_analyzer を設定します
name_yomigana: { type: 'string', store: true, index: 'analyzed', analyzer: 'yomigana_analyzer' }
}
}
}
}
検索文言の正規化
人名のユーザ辞書は読みがなをカタカナで保持しているので、検索文言がひらがなの場合は、カタカナに変換して検索する必要があります。