UUUMに入って2か月のkitabatakeです。 任される仕事が多くなってきて、面白くなってきたと思う一方、ちゃんと捌ききれるかどうかのせめぎ合いの日々です。
弊社のシステムでも使っている、Elasticsearch の基本的な使い方と、弊社内での利用方法など書いてみたいと思います。
Elasticsearchとは?
全文検索エンジン
大量にあるドキュメントデータの中から、目的のワードを含むドキュメントデータを検索する。 検索するということに特化したミドルウェア
RDBMSとの違い
「RDBMS」は、検索条件にマッチするデータ取得できる。
Elasticsearchのような「全文検索エンジン」は、「検索条件との関係性/関連性が高いデータを抽出して返す」という特徴がある。
RDBMSにも全文検索機能が搭載されているものもあるが、検索機能に最適化されている分、パフォーマンスや機能性は一般的に「全文検索エンジン」の方が優れている。
データの登録から関連性の紐付けについての概要
下記資料のサザエさんのところにかなり解りやすく説明されています
ElasticSearch入門 - Speaker Deck
実際に試してみる
弊社のクリエイターポータルサイト「CREAS」では素材の検索にElasticsearchを使っています。 同じようなケースの想定で、解りやすくミニマムのデータ構造で試してみます。 Elasticsearchがインストール済みの想定で進めます。
簡易素材データ
- ID
- name
- description
Indexの作成
Indexとは?
データを保存する場所。 RDBMSのdatabaseのようなもの
indexの一覧を確認するコマンド
curl -XGET 'localhost:9200/_aliases?pretty' --- {}
?pretty はレスポンスのjsonを見やすく表示させるオプションです。 まだ何もしていないので、空のレスポンスが返ってきます。
indexを作成します。今回はindex名を material_test
とします。
curl -XPOST 'http://localhost:9200/material_test'
でindex一覧を確認
curl -XGET 'localhost:9200/_aliases?pretty' --- { "material_test" : { "aliases" : { } } }
作れました。
Mapping
外部データをElasticsearch上でどのようなスキーマとして表現するか定義することをmappingと呼びます。 (Elasticsearchチュートリアル - 不可視点より。参考: Mapping のイメージ)
マッピングの確認
curl -XGET 'localhost:9200/material_test/_mapping?pretty' --- { "material_test" : { "mappings" : { } } }
Elasticsearchはデータを投入すると、自動的にマッピングを作成してくれるので、まずはそれを試してみます。 データを投入します。
curl -XPUT 'localhost:9200/material_test/material/1' -d '{ "title" : "アラビアンジュエル", "description" : "アラビアンナイトな雰囲気の怪しい楽曲です。 ゲームなどの、砂漠の街のイメージなどにおすすめです。" }'
で、Mappingの確認
curl -XGET 'localhost:9200/material_test/_mapping?pretty' --- { "material_test" : { "mappings" : { "material" : { "properties" : { "description" : { "type" : "string" }, "title" : { "type" : "string" } } } } } }
name, descriptionのtypeがstringと定義されました。 これでデータが検索できる状態になったので、 WEB UIから検索等のできるプラグイン
を使って試してみよう。
インストール後に、 http://localhost:9200/_plugin/inquisitor/#/queries にアクセスします。
Queriesタブで検索クエリーの発行、Analyzersタブで文章の解析結果が確認できます。
Queries画面
Analyzers画面
自動的に生成されたmappingでは日本語をうまく解析できていないですね。
いい感じに検索できるようにする
日本語対応 analyzer kuromoji(http://www.atilika.org/) というものがあり、これを設定することで、日本語の検索がいい感じになります。
mappingを手動で設定するコマンド
curl -XPUT 'localhost:9200/material_test' -d ' { "settings": { "analysis": { "filter": { "pos_filter": {type: "kuromoji_part_of_speech", stoptags: ["助詞-格助詞-一般", "助詞-終助詞"]}, "greek_lowercase_filter": {type: "lowercase", language: "greek"} }, "tokenizer": { "kuromoji": { "type": "kuromoji_tokenizer" }, "ngram_tokenizer": { "type": "nGram", "min_gram": "2", "max_gram": "3", "token_chars": [ "letter", "digit" ] } }, "analyzer": { "kuromoji_analyzer": { "type": "custom", "tokenizer": "kuromoji_tokenizer", "filter": [ "kuromoji_baseform", "pos_filter", "greek_lowercase_filter", "cjk_width" ] }, "ngram_analyzer": { "tokenizer": "ngram_tokenizer" } } } }, "mappings" : { "material" : { "properties" : { "name" : { "type" : "string", "analyzer": "kuromoji_analyzer" }, "description" : { "type" : "string", "analyzer": "kuromoji_analyzer" } } } } }'
急に複雑になってしまったので、細かく説明していきます。
まず Analyzerとは?
Analyzer = Tokenizer + Filter です。
Tokenizer?
文字を分割するもの
ngram_tokenizer:
最小と最大の文字と対象にする文字の種類を設定し、分割する
kuromoji_tokenizer:
日本語に対応したプラグインのtokenizer
Filter?
分割した文字を整形したり、フィルタリングするもの
kuromoji_baseform
動詞と形容詞を原型に戻す。
pos_filter
特定の品詞を除外し、マッチ精度を高める
greek_lowercase_filter
英字を小文字化する
cjk_width
全角英数字を半角に直し、半角カタカナを全角に直すなど、漢字圏の文字の全半角を整え、マッチしやすくする
設定を確認する
Analyzer, Tokenizer, Filterが何かわかったところで、上の設定のjsonを見ると、 settingsのところに、tokenizer, filter が設定されていて、それをanalyzerに設定している。 mappingsのところで、各フィールドにanalyzerを指定している、というのが解ると思います。
コマンド実行後にmappingを確認
curl -XGET 'localhost:9200/material_test/_mapping?pretty' --- { "material_test" : { "mappings" : { "material" : { "properties" : { "description" : { "type" : "string", "analyzer" : "kuromoji_analyzer" }, "name" : { "type" : "string", "analyzer" : "kuromoji_analyzer" } } } } } }
文章の解析がいい感じになりました。
以上が基本的な使い方でした。
弊社サービス「CREAS」ではどのように使っているか
CREASではクリエイター向けに様々な動画作成向けの素材を提供しています。
フレームワークはSymfonyを使っていて、
SymfonyでElasticsearchを簡単に扱えるFOSElasticaBundleの紹介 - UUUM攻殻機動隊(エンジニアブログ)
同ブログのnazo様の記事にSymfonyからElasticsearchをどのように使っているかが解りやすくまとめられています。
app/config/elastica.yml
に 上で話していた、analyzerなどの設定が書かれているので、見てみましょう。
settings部分抜粋
settings: index: analysis: tokenizer: kuromoji: type: kuromoji_tokenizer analyzer: kuromoji_analizer: type: custom tokenizer: kuromoji filter: - kuromoji_baseform - lowercase
kuromoji_analyzerが設定されています。
materialのmapping部分抜粋
material: mappings: id: type: integer index: not_analyzed download_count: type: integer index: not_analyzed type: type: string index: not_analyzed material_categories: type: nested properties: id: type: integer index: not_analyzed material_tags: type: nested properties: id: type: integer index: not_analyzed name: type: string index: not_analyzed name: type: string index: analyzed analyzer: kuromoji_analizer description: type: string index: analyzed analyzer: kuromoji_analizer artist_name: type: string index: analyzed analyzer: kuromoji_analizer artist_name_not_analyzed: type: string index: not_analyzed property_path: artist_name artist_katakana: type: string index: not_analyzed is_downloadable: type: boolean index: not_analyzed
name, descriptionなどはkuromoji_analyzerが設定されています。
id_downloadableなどのフラグは解析してもしょうがないので、 index: not_analzyed の指定がされています。 これにより、単純なフラグのon/offでの検索に対応することができます。
クエリ発行箇所
音源素材のtypeと、検索ワードから関連するデータを取得するメソッドの内容を一部抜粋
/** * キーワード検索結果でページネーション * * @param string $type MaterialType * @param string $word 検索キーワード * @return FOS\ElasticaBundle\Paginator\TransformedPaginatorAdapter */ public function findPaginatedByKeywords($type, $word) { ... [ 'match' => [ 'is_downloadable' => true, ] ], [ 'query_string' => [ 'fields' => ['name', 'description', 'artist_name'], 'query' => '"' . $word . '"', ], ], [ 'match' => [ 'type' => $type, ] ], ... return $this->elastica->createPaginatorAdapter('Material', $query); }
ダウンロードが可能(is_downloadable)で、対象の音源素材の種別から、 name, description, artist_name と検索ワードの関連があるデータを取得しているということが、わかると思います。
まとめ
kuromojiのfilterやtokenizerがよくできていたり、各種プラグインがよくできているので、触っているだけで結構面白くて、全文検索に対する理解も深まるのでオススメです!
参考にさせていただいたサイト
https://www.elastic.co/guide/en/elasticsearch/guide/current/mapping-intro.html
http://tech.gmo-media.jp/post/70245090007/elasticsearch-kuromoji-japanese-fulltext-search
https://medium.com/hello-elasticsearch/elasticsearch-833a0704e44b#.lfzf2jxuk