ちょっと話題の記事

Amazon Elasticsearch Serviceで始める 全文検索 入門

2016.09.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

以前から全文検索システムのElasticsearchに興味があったのですが、サーバを構築したりするのに手間がかかるため試してみたことありませんでした。1年ほど前ですがAWSのElasticsearch Serviceというサービスがリリースされ、これを使えばElasticsearchを簡単に試すことができるようになりましたので今更ですがやってみました。Elasticsearchの入門記事はいくつか見つけたのですが、全文検索の話からしている記事があまりなかったので調べたことをまとめます。

全文検索とは何なのか

まずは全文検索を理解するところから始めます。 全文検索に関してはWikipediaでは以下のように説明されていました。

全文検索(ぜんぶんけんさく、Full text search)とは、コンピュータにおいて、複数の文書(ファイル)から特定の文字列を検索すること。「ファイル名検索」や「単一ファイル内の文字列検索」と異なり、「複数文書にまたがって、文書に含まれる全文を対象とした検索」という意味で使用される。

上のWikipediaの説明を見ると「複数の文書(ファイル)」といった言葉があります。ファイルという言葉はあまり気にしない方が理解しやすいのかなと思いました。 RDBに例えると分かりやすいです。文字列型のカラム1つだけ持ったテーブルをイメージしてみて下さい。このテーブルの1レコードが1文書になります。 やっていることとしては、このテーブルのカラムを like '%[検索ワード]%' の条件で検索し、検索ワードを含むレコード(文書)を抽出するのに近いのではないでしょうか。

インデックスとは

全文検索システムというのは上で説明したように特定の文字列がどの文書に含まれているかを検索するシステムのことです。 効率よく検索するために、文書を登録する際にインデックスを作成しています。

インデックスと何かというと特定の文字列が、どの文書に含まれているか、という情報です。インデックスの作成の仕方は全文検索システムによると思いますが、今回は形態素解析という方法を例に挙げて説明します。形態素解析では辞書データを使って文書を単語に分解し、その単語がどの文書に含まれているかという表を作成します。

以下のような2つの文書を全文検索システムに登録したとします。 IDが1の文書 "私は公園に行きます" IDが2の文書 "私は映画鑑賞に行きます"

2つの文書を単語ごとに分解し、以下のようなどの単語がどのドキュメントに含まれているか、という表にします。この表がインデックスです。 この表をもとに文書を探します。この例では分かりやすくするために出てきた単語の順番で書いていますが、実際は探しやすいように単語でソートされています。

単語 文書のID
1、2
1、2
公園
1、2
行き 1、2
ます 1、2
映画
鑑賞

このインデックスを元に文書を探します。 "私"で検索した場合はIDが1と2の文書を返します。 "公園"で検索した場合はIDが1の文書を返します。 "映画"で検索した場合はIDが2の文書を返します。

ソートされたインデックスだけ見れば文書全体を見なくても、どの文書に検索対象の文字列が含まれているか分かるので高速に処理できます。

インデックスの作成に関して

形態素解析の場合、辞書にない新語などが含まれているとうまく分解できない可能性があります。インデックスを作成する方法としては他に文字数で分割するN-Gramといった方法があります。それぞれの方法に長所短所があります。興味がある方は調べてみてください。因みに英語などスペースで分かち書きされている言語の場合は単語の分割は簡単になります。

全文検索システムを使ってみよう

全文検索が何なのか大体理解できたと思いますので実際に全文検索システムに触れてみようと思います。今回はAWSのElasticsearch Serviceというサービスを使います。

Amazon Elasticsearch Serviceとは

Amazon Elasticsearch Service(以下、Amazon ES) とは Elasticsearch という全文検索のシステムをAWS上で使えるサービスになります。 自分で構築する場合と比べ一部制約などはありますが、マネージドなサービスなので簡単に使えます。

Amazon ESとElasticsearchの特徴は以下のページに書いてあります。 Amazon ESを構築する手順も書いているので構築するところまでやってみましょう。日本語のデータを登録したいのでデータはまだ登録しなくていいです。

AWS再入門 Amazon Elasticsearch Service編

Elasticsearchに関しては上のリンクのページにも詳しく書いてありますが、とりあえず以下の特徴だけ押さえておけばいいでしょう。

  • 入出力がJSON形式です。
  • RESTful APIで操作します。GETは検索、PUT、POSTは登録。今回はcurlを使って試します。
  • JSON形式でデータ登録すると自動でフィールド情報(RDBでいうカラム)を定義してくれます。

Elasticsearchの用語を覚える

Elasticsearch の使う用語になれていないと思いますが、RDBでいいかえると以下のようなイメージです。 実はRDBに触ったことがあればそんなに難しくありません。

Elasticsearch RDB
インデックス データベース
タイプ テーブル
フィールド カラム
ドキュメント レコード

日本語で全文検索できるようにする

次にElasticsearch で日本語で全文検索するためのkuromojiプラグインを有効にします。以下のコマンドのEndpointとIndex名の部分を書き換えて実行して下さい。EndpointはManagement Consoleに表示されているものです。Index名は何でもいいです。今回はdiaryにしています。最後の行は実行結果なので最後から2行目までをコピーして実行してみてください。EndpointはManagement Consoleを確認してください。

$ curl -XPOST 'http://search-full-text-search-sample-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com/diary/' -d ' {
    "index":{
        "analysis":{
            "tokenizer" : {
                "kuromoji" : {
                    "type" : "kuromoji_tokenizer"
                }
            },
            "analyzer" : {
                "analyzer" : {
                    "type" : "custom",
                    "tokenizer" : "kuromoji"
                }
            }
        }
    }
}'
{"acknowledged":true}

文書を登録してみよう

全文検索するためにまずは文書を登録してみましょう。JSON形式でさらにフィールドとかいう概念が出てくるので何が1文書なのかよくわからなくなりそうですが、1つのJSONが1文書(ドキュメント)と考えればいいかと思います。以下のようなデータであれば3つの文書になります。ファイルで文書を登録することもできます。1つのファイルに複数文書を入れることもできます。ファイル数と文書の数は関係ありません。

{"user_id": 1, "user_name": "yamada"}
{"user_id": 2, "user_name": "tanaka"}
{"user_id": 3, "user_name": "suzuki"}

文書を登録するには以下のコマンドを実行します。XPOSTとXPUTの2種類がありますがドキュメントのIDを自分で指定したい場合はXPUT、IDを自動でつけてもらいたいときはXPOSTにします。

・XPOSTの場合
$ curl -XPOST 'http://[Endpoint]/[Index名]/Type名]/' -d 'JSONのデータ'

・XPUTの場合
$ curl -XPUT 'http://[Endpoint]/[Index名]/[Type名]/[ID]' -d 'JSONのデータ'

それでは登録してみましょう。Endpointは先ほどと同じにして下さい。TYPEはdiary2016にしました。7行目と14行目は実行結果になりますので実行しないでください。実行結果のJSONには文書のIDが含まれてるのが確認できます。

$ curl -XPOST 'http://search-full-text-search-sample-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com/diary/diary2016' -d '
{
   {
     "diary-date"  : "2016-09-10",
     "diary-text"  : "私はポケモンGOで遊ぶために公園に行きました"
   }'
{"_index":"diary","_type":"diary2016","_id":"AVcsZvli42VqnKK_RRmd","_version":1,"_shards":{"total":2,"successful":1,"failed":0},"created":true}

$ curl -XPOST 'http://search-full-text-search-sample-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com/diary/diary2016' -d '
{
    "diary-date"  : "2016-09-11",
    "diary-text"  : "私はシン・ゴジラを鑑賞するために映画館に行きました"
}'
{"_index":"diary","_type":"diary2016","_id":"AVcsZ0eb42VqnKK_RRme","_version":1,"_shards":{"total":2,"successful":1,"failed":0},"created":true}

間違った場合は以下のコマンドでインデックスを削除できます。2行目は実行結果です。

$ curl -XDELETE 'http://search-full-text-search-sample-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com/diary/'
{"acknowledged":true}

文書を登録後にManamgent Consoleを見るとインデックスやタイプ、フィールドなどの情報が確認できるようになっています。

full-text-search-sample-1

文書を検索してみよう

検索ワードを指定して文書を検索するには以下のコマンドを実行します。

$curl -XGET 'http://[Endpoint]/[Index名]/Type名]/_search' -d '
{
    "query":{"match":{"[フィールド名]":"[検索したい文字列]"}}
}'

検索に関しては多機能のためこれ以上は触れませんので、もっと詳しく知りたい方は以下の記事をご覧ください。 Elasticsearch 入門 検索の基本中の基本

それでは登録したデータを検索してみましょう。この例ではdiary-textに"鑑賞"という文字列が含まれている文書を検索しています。検索結果が正しく表示されているのを確認できます。

$ curl -XGET 'http://search-full-text-search-sample-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com/diary/diary2016/_search' -d '
{
    "query":{"match":{"diary-text":"鑑賞"}}
}'
{"took":28,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.081366636,"hits":[{"_index":"diary","_type":"diary2016","_id":"AVcsZ0eb42VqnKK_RRme","_score":0.081366636,"_source":
{
    "diary-date"  : "2016-09-11",
    "diary-text"  : "私はシン・ゴジラを鑑賞するために映画館に行きました"
}}]}}

全文検索の特徴

全文検索の特徴としては検索したい文字列が完全に含まれていなくても抽出されることにあると思います。 以下の例のように"するために"という文字列で検索してみて下さい。"私はポケモンGOで遊ぶために公園に行きました"という文書には"するために"という文字列は含まれていませんが、"ために"という文字列は含まれているので検索結果に含まれています。6行目以降は検索結果になります。

$  curl -XGET 'http://search-full-text-search-sample-**********.ap-northeast-1.es.amazonaws.com/diary/diary2016/_search' -d '
{
 {
   "query":{"match":{"diary-text":"するために"}}
 }'
{"took":29,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":2,"max_score":0.14996772,"hits":[{"_index":"diary","_type":"diary2016","_id":"AVcsZ0eb42VqnKK_RRme","_score":0.14996772,"_source":
{
    "diary-date"  : "2016-09-11",
    "diary-text"  : "私はシン・ゴジラを鑑賞するために映画館に行きました"
}},{"_index":"diary","_type":"diary2016","_id":"AVcsZvli42VqnKK_RRmd","_score":0.031316765,"_source":
{
    "diary-date"  : "2016-09-10",
    "diary-text"  : "私はポケモンGOで遊ぶために公園に行きました"
}}]}}

どれくらい一致しているかはスコアで評価されます。結果のJSONの_scoreという項目です。すべて含んでいる文書が0.14996772なのに対し、一部しか含んでいない文書は0.031316765になっておりスコアが低くなっているのを確認できます。

最後に

調査した内容は以上になります。Amazon ESを使って全文検索に触れることができました。とりあえずElaticsearchの入門記事が読めるくらいにはなったと思います。 昨日初めて触ったので間違っている個所があるかもしれませんが、もし見つけたら教えてください。Amazon ESや全文検索システムに触れたことがないかたはすぐに試せるのでぜひ試してみてください。次はKibanaなどの可視化ツールやICUなどのプラグインに関して使っていきたいと思います。

Elaticsearchに関してさらに詳しくなりたい方は弊社ブログのElasticsearch 入門シリーズがお勧めです。

  • インデックスの設計
  • データスキーマの設計
  • Elaticsearchの使いどころ

などの内容が詳しく書かれています。