さて、前回に続き Elasticsearch での検証です。
★関連記事
Elasticsearch 6 を使ったデータ検証 その1(Dockerでコンテナの作成と確認)
Elasticsearch 6 を使ったデータ検証 その3(bulkでデータを投入してみる)
Elasticsearch 6 を使ったデータ検証 その4(チュートリアル記事とデータの検索での比較)
Elasticsearch 6 を使ったデータ検証 その5(クエリでの検索)
Elasticsearch 6 を使ったデータ検証 その6(Aggregationを使った分類・集計)
Elasticsearch 6 を使ったデータ検証 その7(Analyzerについて)
ある程度のデータセットを試して見たいんで既存でテータ提供しているものを利用させていただきます。
そこで実施してみようと思ったところで以下のサイト。
★Elasticsearchチュートリアル
http://code46.hatenablog.com/entry/2014/01/21/115620
「livedoor グルメの研究用データセット」を使ったデータの検証が行われています。
同じようなことやってもと思いつつも、こちら「2014-01-21」の記事であります。
バージョンアップの早いソフトウェアであるElasticsearchですのでどのぐらい違いがあるのか試してみました。
まず、こちらの記事との大きな違いとして以下の3点があります。
- Analyzerでkuromojiを利用
- elasticsearch6ではインデックス内に複数のTypeを設定できないのでインデックスを2つに分けてみる。
- elasticsearch6ではデータ型で「string」は利用できないので変更。
このあたりで思ったことはWebでの説明でRDBMSとの比較で、「インデック = DB」、「タイプ = Table」というのを見かけましたが、正直あまり正しくなく、寧ろ理解の妨げになってしまうのかなと感じました。
データ型について
Elasticsearch6では「String」のデータ型は廃止になっています。
Elasticsearch5から「String」は非推奨となりましたが6系ではエラーとなります。
かわりに「text型」、「keyword型」を利用します。
主なデータ型は以下です。
text型
text型は、文字列からなる文章を格納するデータ型です。
text型のフィールドは、格納時にアナライザ(Analyzer)によって文章を構成する各単語に分割され、分割された単語ごとに転置インデックスが構成されるという特徴があります。
keyword型
keyword型は、text同様に文字列を格納できます。
text型との大きな違いとして、keyword型は格納した文字列を[完全一致]で検索する用途で使います。
keywordに格納したデータはアナライザによる単語分割処理が行われません。
たとえば、「メールアドレス」や「WebサイトのURL」、「タグ分類を行う際のタグ名」といったデータは、分割せずに格納・検索したいケースが多いはずです。
数値データの型
long、short、integer、float
日付データの型
date
boolean型
trueまたはfalseのみを指定できます。
Elasticsearch 5系まで Boolean type のフィールドに 0/1、on/off、yes/no、true/false など多くの値を設定することが可能でしたが、Elasticsearch 6系からは true/false の指定のみとなりました。
"true"/"false"(文字化)でも利用できます。
マッピングを行う
Elasticsearchでドキュメント内のデータ構造やデータ型を定義したもものをマッピングと呼びます。
マッピングはリクエストボディに「mappings」句を書いて定義することができます。
「mappings」句の中ではドキュメントタイプの名前、及び、そこに含まれる各データ型定義(フィールド名及びデータ型」を「properties」句の中に定義します。
マッピングの定義ですが、一度定義したマッピングは変更することができないので注意が必要です。
変更する場合はインデックス毎削除して再作成の必要があります。(もちろんデータの移行も)
そのためマッピングの作成はある程度の注意が必要です。
ただ、フィールドの追加はできます。
さて、今回マッピングの作成はファイルから実施してみます。
まず以下のファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "settings": { "analysis": { "analyzer": { "my_kuromoji_analyzer": { "type": "custom", "tokenizer": "kuromoji_tokenizer" } } } } } |
今回、日本語を使うということで上記のファイルを使ってインデックスの作成を行います。
まずはrestaurantからインデックスを作成します。
1 2 3 4 5 6 7 8 |
$ curl -H "Content-Type: application/json" -X PUT 'http://localhost:9200/restaurant?pretty' -d @kuromoji_setting.json { "acknowledged" : true, "shards_acknowledged" : true, "index" : "restaurant" } |
続いてratingもインデックスを作成します。
1 2 3 4 5 6 7 8 |
$ curl -H "Content-Type: application/json" -X PUT 'http://localhost:9200/rating?pretty' -d @kuromoji_setting.json { "acknowledged" : true, "shards_acknowledged" : true, "index" : "rating" } |
さて、マッピング作成用に以下の2つのファイルを準備しました。
こちらでマッピングの作成を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
{ "properties": { "name": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "name_alphabet": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "name_kana": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "address": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "description": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "purpose": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "category": { "type": "text", "analyzer": "whitespace" }, "photo_count": { "type": "long" }, "menu_count": { "type": "long" }, "access_count": { "type": "long" }, "closed": { "type": "boolean" }, "location": { "type": "geo_point", "store": "true" } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
{ "properties": { "rating_id": { "type": "long" }, "restaurant_id": { "type": "long" }, "total": { "type": "long" }, "food": { "type": "long" }, "service": { "type": "long" }, "atmosphere": { "type": "long" }, "cost_performance": { "type": "long" }, "title": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "body": { "type": "text", "analyzer": "my_kuromoji_analyzer" }, "purpose": { "type": "long" }, "created_on": { "type": "date" } } } |
さて、こちらのマッピングを登録したいと思います。
まずは「restaurant」から。
1 2 3 4 5 6 |
$ curl -H "Content-Type: application/json" -X PUT 'http://localhost:9200/restaurant/_mapping/type?pretty' -d @mapping_restaurant.json { "acknowledged" : true } |
インデックス名が「restaurant」でタイプ名が「type」で登録されました。
さらっと書いていますが、Elasticsearch6では「Content-Type」の設定が必要なのでそちらをリクエストに追加しています。
こちらのインデックスの状況を確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
$ curl -H "Content-Type: application/json" -X GET 'localhost:9200/restaurant?pretty' { "restaurant" : { "aliases" : { }, "mappings" : { "type" : { "properties" : { "access_count" : { "type" : "long" }, "address" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "category" : { "type" : "text", "analyzer" : "whitespace" }, "closed" : { "type" : "boolean" }, "description" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "location" : { "type" : "geo_point", "store" : true }, "menu_count" : { "type" : "long" }, "name" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "name_alphabet" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "name_kana" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "photo_count" : { "type" : "long" }, "purpose" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" } } } }, "settings" : { "index" : { "number_of_shards" : "5", "provided_name" : "restaurant", "creation_date" : "1534134035460", "analysis" : { "analyzer" : { "my_kuromoji_analyzer" : { "type" : "custom", "tokenizer" : "kuromoji_tokenizer" } } }, "number_of_replicas" : "1", "uuid" : "PMxbO-DkRb2ae9pOKNdRkQ", "version" : { "created" : "6030299" } } } } } |
無事登録されているようですね。
さて、同じように「rating」のマッピングを行います。
1 2 3 4 5 6 |
$ curl -H "Content-Type: application/json" -X PUT 'http://localhost:9200/rating/_mapping/type?pretty' -d @mapping_rating.json { "acknowledged" : true } |
インデックス名が「rating」でタイプ名が「type」で登録されています。
同じようにインデックスの状況を確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
$ curl -H "Content-Type: application/json" -X GET 'localhost:9200/rating?pretty' { "rating" : { "aliases" : { }, "mappings" : { "type" : { "properties" : { "atmosphere" : { "type" : "long" }, "body" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "cost_performance" : { "type" : "long" }, "created_on" : { "type" : "date" }, "food" : { "type" : "long" }, "purpose" : { "type" : "long" }, "rating_id" : { "type" : "long" }, "restaurant_id" : { "type" : "long" }, "service" : { "type" : "long" }, "title" : { "type" : "text", "analyzer" : "my_kuromoji_analyzer" }, "total" : { "type" : "long" } } } }, "settings" : { "index" : { "number_of_shards" : "5", "provided_name" : "rating", "creation_date" : "1534134083619", "analysis" : { "analyzer" : { "my_kuromoji_analyzer" : { "type" : "custom", "tokenizer" : "kuromoji_tokenizer" } } }, "number_of_replicas" : "1", "uuid" : "0LM_HIjpTVKFMgoeBnFCnA", "version" : { "created" : "6030299" } } } } } |
こちらで無事、マッピングと確認が実施できました。
全体的なインデックスの状態は以下になります。
1 2 3 4 5 6 |
$ curl 'http://localhost:9200/_cat/indices?v' health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open rating 0LM_HIjpTVKFMgoeBnFCnA 5 1 0 0 1.2kb 1.2kb yellow open restaurant PMxbO-DkRb2ae9pOKNdRkQ 5 1 0 0 1.2kb 1.2kb |
そしてそれぞれのステータスは以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
$ curl 'http://localhost:9200/restaurant/_stats/indexing?pretty' { "_shards" : { "total" : 10, "successful" : 5, "failed" : 0 }, "_all" : { "primaries" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } }, "total" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } } }, "indices" : { "restaurant" : { "primaries" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } }, "total" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
$ curl 'http://localhost:9200/rating/_stats/indexing?pretty' { "_shards" : { "total" : 10, "successful" : 5, "failed" : 0 }, "_all" : { "primaries" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } }, "total" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } } }, "indices" : { "rating" : { "primaries" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } }, "total" : { "indexing" : { "index_total" : 0, "index_time_in_millis" : 0, "index_current" : 0, "index_failed" : 0, "delete_total" : 0, "delete_time_in_millis" : 0, "delete_current" : 0, "noop_update_total" : 0, "is_throttled" : false, "throttle_time_in_millis" : 0 } } } } } |
長くなったので次回にデータの登録を行なってみます。
★関連記事
Elasticsearch 6 を使ったデータ検証 その1(Dockerでコンテナの作成と確認)
Elasticsearch 6 を使ったデータ検証 その3(bulkでデータを投入してみる)
Elasticsearch 6 を使ったデータ検証 その4(チュートリアル記事とデータの検索での比較)
Elasticsearch 6 を使ったデータ検証 その5(クエリでの検索)
Elasticsearch 6 を使ったデータ検証 その6(Aggregationを使った分類・集計)
Elasticsearch 6 を使ったデータ検証 その7(Analyzerについて)
このブログは株式会社CoLabMixによる技術ブログです。
GCP、AWSなどでのインフラ構築・運用や、クローリング・分析・検索などを主体とした開発を行なっています。
Ruby on RailsやDjango、Pythonなどの開発依頼などお気軽にお声がけください。
開発パートナーを増やしたいという企業と積極的に繋がっていきたいです。
お問い合わせやご依頼・ご相談など