LoginSignup
55
54

More than 5 years have passed since last update.

elasticsearchで人名漢字の読みがな検索を行う

Last updated at Posted at 2015-02-25

TL;DR

漢字で保持しているユーザの名前をひらがな、カタカナでも検索できるようにします。

はたらくを面白くするビジネスSNS・Wantedly_と_プロトタイピング導入がもたらす5つのメリット~UI_Crunch__3レポ(DeNA×グッドパッチ×クックパッド×スタンダード×グリー)_-_エンジニアtype.jpg

使ってみる

以下のURLにアクセスして、上部のサーチボックスから検索が行えます。
ログインするとよりたくさんの結果が返ります。お試しください。

環境

方針

  • 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' }
      }
    }
  }        
}

検索文言の正規化

人名のユーザ辞書は読みがなをカタカナで保持しているので、検索文言がひらがなの場合は、カタカナに変換して検索する必要があります。

参考

謝辞

55
54
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
54