Edited at

はじめての Elasticsearch

この記事は全文検索エンジン「Elasticsearch」の入門チュートリアルです。


Elasticsearch とは

Elasticsearch は Elastic 社が開発しているオープンソースの全文検索エンジンです。

大量のドキュメントから目的の単語を含むドキュメントを高速に抽出することができます。

Elasticsearch では RESTful インターフェースを使って操作しますが、「Elasticsearch SQL」を使って SQL 文でクエリを記述することもできます。

Oracle や MySQL などのリレーショナルデータベースに慣れている人にとっては、最初はとっつきにくいと感じるかもしれません。

しかし、Elasticsearch の API はとてもシンプルなので、心配しなくても大丈夫です。


Elastic Stack とは

Elastic Stack は Elasticsearch 関連製品の総称です。

バージョン 2.x 以前は「ELK」と呼ばれていましたが、バージョン 5 からは名称を改め「Elastic Stack」となりました。

製品名
機能

Elasticsearch
ドキュメントを保存・検索します。

Kibana
データを可視化します。

Logstash
データソースからデータを取り込み・変換します。

Beats
データソースからデータを取り込みます。

X-Pack
セキュリティ、モニタリング、ウォッチ、レポート、グラフの機能を拡張します。

※Logstash については、別記事「はじめての Logstash」を参照してください。


動作環境


  • Mac OS X 10.14.4

  • Java 11.0.2

  • Elasticsearch 7.0.0

  • Kibana 7.0.0


インストール

Mac のターミナルを開いて、必要なソフトウェアをインストールします。

Elasticsearch をダウンロードするのに wget コマンドを使用しますので、インストールしておきます。

$ brew install wget

Elasticsearch の動作に Java(バージョン 1.8.0_131 以上) が必要になるので、インストールしておきます。

Java SE Downloads

https://www.oracle.com/technetwork/java/javase/downloads/index.html

$ java -version

Elasticsearch と日本語検索プラグイン「 kuromoji 」をインストールします。

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.0.0-darwin-x86_64.tar.gz

$ tar -xzf elasticsearch-7.0.0-darwin-x86_64.tar.gz
$ cd elasticsearch-7.0.0
$ bin/elasticsearch-plugin install analysis-kuromoji

Elasticsearch を起動します。

$ bin/elasticsearch

ブラウザを開いて、http://localhost:9200/ にアクセスしてみましょう。

以下の JSON が表示されれば、起動は成功しています。

{

"name" : "ootashigekinoMac-mini.local",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "Iu-RzOljQkituZ2DCuHvMw",
"version" : {
"number" : "7.0.0",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "b7e28a7",
"build_date" : "2019-04-05T22:55:32.697037Z",
"build_snapshot" : false,
"lucene_version" : "8.0.0",
"minimum_wire_compatibility_version" : "6.7.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}

続いて、Kibana をインストールします。

wget https://artifacts.elastic.co/downloads/kibana/kibana-7.0.0-darwin-x86_64.tar.gz

tar -xzf kibana-7.0.0-darwin-x86_64.tar.gz
cd kibana-7.0.0-darwin-x86_64/

Kibana を起動します。

$ bin/kibana

ブラウザを開いて、http://localhost:5601/ にアクセスしてみましょう。

以下のような画面が表示されれば、起動は成功しています。

kibana_7_0_0_home.png


チュートリアルをはじめる前に

この記事で使用しているコマンドは GitHub で公開していますので参考にしてください。

https://github.com/nskydiving/elasticsearch_examples

Elasticsearch は RESTful インターフェースで操作できますが、このチュートリアルでは Kibana の「Dev Tools」を使用します。

Kibana のメニューの中から「Dev Tools」(レンチアイコン)を選択してください。

以下のような画面が表示されます。

kibana_7_0_0_dev_tools.png

Console の左側エリアにコマンドを入力して実行ボタン(緑色の再生ボタン)をクリックすると、選択されたコマンドが実行され、右側エリアに実行結果が表示されます。

試しに以下のコマンドを実行してみましょう。

GET _search

{
"query": {
"match_all": {}
}
}

右側エリアに実行結果が表示されれば成功です。

※Mac 以外の環境の人は以下のリンクを参照してください。

https://www.elastic.co/jp/downloads/elasticsearch

https://www.elastic.co/jp/downloads/kibana


CRUDオペレーション(RESTful API)

Elasticsearch ではリレーショナルデータベースとは異なる用語を使用しますが、概ね以下のように理解しておけば良いでしょう。

Elasticsearch
リレーショナルデータベース

Index
データベース

Type
テーブル

Document
レコード

※バージョン 6 以降では、「Type」の指定が非推奨となり、タイプ名の代わりに「_doc」を指定するようになりました。


 ドキュメントを作成

ドキュメントを作成するには、PUT で「/インデックス/タイプ/ドキュメントID」にアクセスし、ドキュメントの中身を JSON で渡します。

コマンド

#    +--- Index name

# | +--- Type name
# | | +--- Document ID
# | | |
# V V V
PUT /library/_doc/1
{
"title": "Norwegian Wood",
"name": {
"first": "Haruki",
"last": "Murakami"
},
"publish_date": "1987-09-04T00:00:00+0900",
"price": 19.95
}

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 15,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 22,
"_primary_term" : 1
}


 ドキュメントを取得

ドキュメントを取得するには、GET で「/インデックス/タイプ/ドキュメントID」にアクセスします。

コマンド

GET /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 15,
"_seq_no" : 22,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Norwegian Wood",
"name" : {
"first" : "Haruki",
"last" : "Murakami"
},
"publish_date" : "1987-09-04T00:00:00+0900",
"price" : 19.95
}
}


 ドキュメントIDを指定せずにドキュメントを作成

ドキュメントIDを指定せずにドキュメントを作成すると、自動的にドキュメントIDが割り当てられます。

自動的に割り当てられたドキュメントIDは実行結果で確認できます。

コマンド

POST /library/_doc/

{
"title": "Kafka on the Shore",
"name": {
"first": "Haruki",
"last": "Murakami"
},
"publish_date": "2002-09-12T00:00:00+0900",
"price": 19.95
}

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "q2aZVmoBFWFSqRl8nY0k",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 23,
"_primary_term" : 1
}


 「ドキュメントIDを指定せずにドキュメントを作成」の確認

コマンド

# POST /library/_doc/ で取得した id を指定してください

GET /library/_doc/q2aZVmoBFWFSqRl8nY0k

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "q2aZVmoBFWFSqRl8nY0k",
"_version" : 1,
"_seq_no" : 23,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Kafka on the Shore",
"name" : {
"first" : "Haruki",
"last" : "Murakami"
},
"publish_date" : "2002-09-12T00:00:00+0900",
"price" : 19.95
}
}


 ドキュメントを上書き更新

ドキュメントを上書き更新するには、PUT で「/インデックス/タイプ/ドキュメントID」にアクセスします。

コマンド

PUT /library/_doc/1

{
"title": "Norwegian Wood",
"name": {
"first": "Haruki",
"last": "Murakami"
},
"publish_date": "1987-09-04T00:00:00+0900",
"price": 29.95
}

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 18,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 26,
"_primary_term" : 1
}


 「ドキュメントを上書き更新」の確認

コマンド

GET /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 18,
"_seq_no" : 26,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Norwegian Wood",
"name" : {
"first" : "Haruki",
"last" : "Murakami"
},
"publish_date" : "1987-09-04T00:00:00+0900",
"price" : 29.95
}
}


 ドキュメントを部分的に更新

ドキュメントを部分的に更新するには、POST で「/インデックス/_update/ドキュメントID」にアクセスし、 JSON で "doc" クエリを指定します。

コマンド

POST /library/_update/1

{
"doc": {
"price": 10
}
}

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 19,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 27,
"_primary_term" : 1
}


 「ドキュメントを部分的に更新」の確認

コマンド

GET /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 19,
"_seq_no" : 27,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Norwegian Wood",
"name" : {
"first" : "Haruki",
"last" : "Murakami"
},
"publish_date" : "1987-09-04T00:00:00+0900",
"price" : 10
}
}


 ドキュメントに項目を追加

コマンド

ドキュメントに項目を追加するには、POST で「/インデックス/_update/ドキュメントID」にアクセスし、JSON で "doc" クエリを指定します。

POST /library/_update/1

{
"doc": {
"price_jpy": 1800
}
}

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 20,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 28,
"_primary_term" : 1
}


 「ドキュメントに項目を追加」の確認

コマンド

GET /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 20,
"_seq_no" : 28,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Norwegian Wood",
"name" : {
"first" : "Haruki",
"last" : "Murakami"
},
"publish_date" : "1987-09-04T00:00:00+0900",
"price" : 10,
"price_jpy" : 1800
}
}


 ドキュメントを削除

ドキュメントを削除するには、DELETE で「/インデックス/タイプ/ドキュメントID」にアクセスします。

コマンド

DELETE /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_version" : 21,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 29,
"_primary_term" : 1
}


 「ドキュメントを削除」の確認

コマンド

GET /library/_doc/1

実行結果

{

"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"found" : false
}


 インデックスを削除

インデックスを削除するには、DELETE で「/インデックス」にアクセスします。

コマンド

DELETE /library

実行結果

{

"acknowledged" : true
}


 「インデックスを削除」の確認

コマンド

GET /libray/_doc/2

実行結果

{

"error" : {
"root_cause" : [
{
"type" : "index_not_found_exception",
"reason" : "no such index [libray]",
"resource.type" : "index_expression",
"resource.id" : "libray",
"index_uuid" : "_na_",
"index" : "libray"
}
],
"type" : "index_not_found_exception",
"reason" : "no such index [libray]",
"resource.type" : "index_expression",
"resource.id" : "libray",
"index_uuid" : "_na_",
"index" : "libray"
},
"status" : 404
}


ドキュメントの検索

インデックスに保存されたドキュメントを検索します。

下準備として、インデックスを一度削除して、テストデータのドキュメントを作成します。

複数のドキュメントを一気に作成するには、POST で「/インデックス/タイプ/_bulk」にアクセスします。

コマンド

DELETE /library

POST /library/_bulk
{"index": {"_id": 1}}
{"title": "The quick brown fox", "price": 5}
{"index": {"_id": 2}}
{"title": "The quick brown fox jumps over the lazy dog", "price": 15}
{"index": {"_id": 3}}
{"title": "The quick brown fox jumps over the quick dog", "price": 8}
{"index": {"_id": 4}}
{"title": "Brown fox and brown dog", "price": 2}
{"index": {"_id": 5}}
{"title": "Lazy dog", "price": 9}


 すべてのドキュメントを検索

すべてのドキュメントを検索するには、GET で「/インデックス/タイプ/_search」にアクセスします。

また、「/library/_search?size=3」のように size パラメータで検索件数を指定することもできます。

コマンド

GET /library/_search

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"title" : "The quick brown fox",
"price" : 5
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"title" : "Brown fox and brown dog",
"price" : 2
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"title" : "Lazy dog",
"price" : 9
}
}
]
}
}


 指定した単語を含むドキュメントを検索

指定した単語を含むドキュメントを検索するには、GET で「/インデックス/_search」にアクセスし、JSON で "match" クエリを指定します。

ここでは "title" に "fox" を含むドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"match": {
"title": "fox"
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 0.32951736,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.32951736,
"_source" : {
"title" : "The quick brown fox",
"price" : 5
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.30488566,
"_source" : {
"title" : "Brown fox and brown dog",
"price" : 2
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.23470737,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.23470737,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
}
]
}
}


 OR条件でドキュメントを検索

OR条件でドキュメントを検索するには、GET で「/インデックス/_search」にアクセスし、JSON で "match" クエリを指定します。

ここでは "title" に "quick" または "dog" を含むドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"match": {
"title": "quick dog"
}
}
}

検索結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 0.8762741,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.8762741,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.6744513,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6173784,
"_source" : {
"title" : "The quick brown fox",
"price" : 5
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.3930218,
"_source" : {
"title" : "Lazy dog",
"price" : 9
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.30488566,
"_source" : {
"title" : "Brown fox and brown dog",
"price" : 2
}
}
]
}
}


 空白を含む単語を含むドキュメントを検索

空白を含む単語を含むドキュメントを検索するには、GET で「/インデックス/_serach」にアクセスし、JSON で 「match_phrase」 クエリを指定します。

ここでは "title" に "quick dog" を含むドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"match_phrase": {
"title": "quick dog"
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.67445135,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.67445135,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
}
]
}
}


 ドキュメント検索のスコアを表示

ドキュメントを検索するときに、指定された単語との関連性がスコアとして計算されます。

コマンドに explain パラメータを指定することで、どのようにスコアが計算されたか確認することができます。

コマンド

GET /library/_search?explain

{
"query": {
"match": {
"title": "quick"
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.64156675,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.64156675,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6173784,
"_source" : {
"title" : "The quick brown fox",
"price" : 5
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.43974394,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
}
]
}
}


 AND条件でドキュメントを検索

AND条件でドキュメントを検索するには、GET で「/インデックス/_search」にアクセスし、JSON で "bool" クエリを指定します。

ここでは、"title" に "quick" と "lazy dog" の両方を含むドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"bool": {
"must": [
{
"match": {
"title": "quick"
}
},
{
"match_phrase": {
"title": "lazy dog"
}
}
]
}
}
}

実行結果

{

"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3887084,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.3887084,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
}
]
}
}


 ドキュメント検索のスコアに重み付け

ドキュメント検索のスコアに重み付けするには、GET で「/インデックス/_search」にアクセスし、JSON で "boost" クエリを指定します。

ここでは、"title" に "quick dog" を含むドキュメントのスコアを半分にしています。

コマンド

GET /library/_search

{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"title": {
"query": "quick dog",
"boost": 0.5
}
}
},
{
"match_phrase": {
"title": {
"query": "lazy dog"
}
}
}
]
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.5890584,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.5890584,
"_source" : {
"title" : "Lazy dog",
"price" : 9
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.9489645,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.33722568,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
}
]
}
}


 ドキュメント検索結果をハイライト表示

ドキュメント検索結果をハイライト表示するには、GET で「/インデックス/_search」にアクセスし、JSON で "highlight" クエリを指定します。

検索でヒットした文字列が em タグで囲われて出力されます。

コマンド

GET /library/_search

{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"title": {
"query": "quick dog",
"boost": 0.5
}
}
},
{
"match_phrase": {
"title": {
"query": "lazy dog"
}
}
}
]
}
},
"highlight": {
"fields": {
"title": {}
}
}
}

実行結果

{

"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.5890584,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.5890584,
"_source" : {
"title" : "Lazy dog",
"price" : 9
},
"highlight" : {
"title" : [
"<em>Lazy</em> <em>dog</em>"
]
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.9489645,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
},
"highlight" : {
"title" : [
"The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"
]
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.33722568,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
},
"highlight" : {
"title" : [
"The quick brown fox jumps over the <em>quick</em> <em>dog</em>"
]
}
}
]
}
}


 フィルタリングしてドキュメントを検索

フィルタリングしてドキュメントを検索するには、GET で「/インデックス/_search」にアクセスし、JSON で "filter" クエリを指定します。

ここでは、"price" が 5 〜 10 のドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"bool": {
"filter": {
"range": {
"price": {
"gte": 5,
"lte": 10
}
}
}
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.0,
"_source" : {
"title" : "The quick brown fox",
"price" : 5
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.0,
"_source" : {
"title" : "The quick brown fox jumps over the quick dog",
"price" : 8
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.0,
"_source" : {
"title" : "Lazy dog",
"price" : 9
}
}
]
}
}


 他のクエリとフィルタリングを組み合わせてドキュメントを検索

他のクエリとフィルタリングを組み合わせてドキュメントを検索するには、GET で「/インデックス/_search」にアクセスし、JSON で 他のクエリと "filter" クエリを合わせて指定します。

ここでは、"title" に "lazy dog" を含み、"price" が 5 以上のドキュメントを検索しています。

コマンド

GET /library/_search

{
"query": {
"bool": {
"must": [{
"match_phrase": {
"title": "lazy dog"
}
}],
"filter": {
"range": {
"price": {
"gte": 5
}
}
}
}
}
}

実行結果

{

"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.5890584,
"hits" : [
{
"_index" : "library",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.5890584,
"_source" : {
"title" : "Lazy dog",
"price" : 9
}
},
{
"_index" : "library",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.9489645,
"_source" : {
"title" : "The quick brown fox jumps over the lazy dog",
"price" : 15
}
}
]
}
}


Mapping

Elasticsearch はスキーマレスのデータベースですが、あらかじめマッピングを設定しておくこともできます。

下準備として、インデックスを一度削除して、テストデータのドキュメントを作成します。

コマンド

DELETE /library

POST /library/_bulk
{"index": {"_id": 1}}
{"title": "The quick brown fox", "price": 5}
{"index": {"_id": 2}}
{"title": "The quick brown fox jumps over the lazy dog", "price": 15}
{"index": {"_id": 3}}
{"title": "The quick brown fox jumps over the quick dog", "price": 8}
{"index": {"_id": 4}}
{"title": "Brown fox and brown dog", "price": 2}
{"index": {"_id": 5}}
{"title": "Lazy dog", "price": 9}


 マッピングを取得

コマンド

GET /library/_mapping

実行結果

{

"library" : {
"mappings" : {
"properties" : {
"price" : {
"type" : "long"
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}


 マッピングを追加

新しいマッピング "my_new_field" を追加します。

コマンド

PUT /library/_mapping

{
"properties": {
"my_new_field": {
"type": "text"
}
}
}

実行結果

{

"acknowledged" : true
}


 「マッピングを追加」の確認

コマンド

GET /library/_mapping

実行結果

{

"library" : {
"mappings" : {
"properties" : {
"my_new_field" : {
"type" : "text"
},
"price" : {
"type" : "long"
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}


 アナライザを設定してマッピングを追加

マッピングに設定するアナライザは "analyzer" クエリを指定します。

コマンド

PUT /library/_mapping

{
"properties": {
"english_field": {
"type": "text",
"analyzer": "english"
}
}
}

実行結果

{

"acknowledged" : true
}


 「アナライザを設定してマッピングを追加」の確認

コマンド

GET /library/_mapping

実行結果

{

"library" : {
"mappings" : {
"properties" : {
"english_field" : {
"type" : "text",
"analyzer" : "english"
},
"my_new_field" : {
"type" : "text"
},
"price" : {
"type" : "long"
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}


 マッピングは変更することはできない

一度追加したマッピングは変更することはできないため、以下のコマンドはエラーとなります。

コマンド

PUT /library/_mapping

{
"properties": {
"english_field": {
"type": "double"
}
}
}

実行結果

{

"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "mapper [english_field] of different type, current_type [text], merged_type [double]"
}
],
"type": "illegal_argument_exception",
"reason": "mapper [english_field] of different type, current_type [text], merged_type [double]"
},
"status": 400
}


 型の違いによる検索結果への影響

「/log/transactions」へ id が「234571」と「1392.223」の2つのドキュメントを追加し、検索条件に「id が 1392 以上」を指定して検索を実行します。

「234571」と「1392.223」の両方が検索にヒットすることが期待されますが、実際には「234571」しかヒットしません。

コマンド

POST /log/_doc

{
"id": 234571
}

POST /log/_doc
{
"id": 1392.223
}

GET /log/_search
{
"query": {
"bool": {
"filter": {
"range": {
"id": {
"gt": 1392
}
}
}
}
}
}

実行結果

# POST /log/transactions

{
"_index" : "log",
"_type" : "_doc",
"_id" : "r2axVmoBFWFSqRl86Y1N",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}

# POST /log/transactions
{
"_index" : "log",
"_type" : "_doc",
"_id" : "sGayVmoBFWFSqRl8S414",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}

# GET /log/transactions/_search
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "log",
"_type" : "_doc",
"_id" : "r2axVmoBFWFSqRl86Y1N",
"_score" : 0.0,
"_source" : {
"id" : 234571
}
}
]
}
}


 「型の違いによる検索結果への影響」の確認

マッピングを取得すると "id" の型は long 型です。

また、すべての "log" インデックスを検索すると、"id" の型が合わない「1392.223」のドキュメントも追加されていることが分かります。

コマンド

GET /log/_mapping

GET /log/_search

実行結果

# GET /log/_mapping

{
"log" : {
"mappings" : {
"properties" : {
"id" : {
"type" : "long"
}
}
}
}
}

# GET /log/_search
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "log",
"_type" : "_doc",
"_id" : "r2axVmoBFWFSqRl86Y1N",
"_score" : 1.0,
"_source" : {
"id" : 234571
}
},
{
"_index" : "log",
"_type" : "_doc",
"_id" : "sGayVmoBFWFSqRl8S414",
"_score" : 1.0,
"_source" : {
"id" : 1392.223
}
}
]
}
}


Analysis

Elasticsearch がどのように文字列を分析しているのか確認することができます。

下準備として、インデックスを一度削除して、テストデータのドキュメントを作成します。

コマンド

DELETE /library

POST /library/_bulk
{"index": {"_id": 1}}
{"title": "The quick brown fox", "price": 5}
{"index": {"_id": 2}}
{"title": "The quick brown fox jumps over the lazy dog", "price": 15}
{"index": {"_id": 3}}
{"title": "The quick brown fox jumps over the quick dog", "price": 8}
{"index": {"_id": 4}}
{"title": "Brown fox and brown dog", "price": 2}
{"index": {"_id": 5}}
{"title": "Lazy dog", "price": 9}


 文字列の分析結果を表示

"Brown fox brown dog" の分析結果を表示します。

"Brown" "fox" "brown" "dog" の 4 つの単語に分解されていることが分かります。 

コマンド

GET /library/_analyze

{
"tokenizer": "standard",
"text": "Brown fox brown dog"
}

実行結果

{

"tokens" : [
{
"token" : "Brown",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "fox",
"start_offset" : 6,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "brown",
"start_offset" : 10,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "dog",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 3
}
]
}


 フィルターを指定して文字列の分析結果を表示

"filter" クエリに "lowercase" を指定すると、文字列を小文字に変換して分析します。

最初の "Brown" が "brown" として分析されていることが分かります。 

コマンド

GET /library/_analyze

{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Brown fox brown dog"
}

実行結果

{

"tokens" : [
{
"token" : "brown",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "fox",
"start_offset" : 6,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "brown",
"start_offset" : 10,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "dog",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 3
}
]
}


 複数のフィルターを指定して文字列の分析結果を表示

"filter" クエリに "lowercase" と "unique" を指定すると、文字列を小文字に変換し、さらに重複する単語を削除して分析します。

"Brown" が小文字に変換され、重複した 2 つ目の "brown" は削除されていることが分かります。

コマンド

GET /library/_analyze

{
"tokenizer": "standard",
"filter": ["lowercase","unique"],
"text": "Brown brown brown fox brown dog"
}

実行結果

{

"tokens" : [
{
"token" : "brown",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "fox",
"start_offset" : 18,
"end_offset" : 21,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "dog",
"start_offset" : 28,
"end_offset" : 31,
"type" : "<ALPHANUM>",
"position" : 2
}
]
}


 トークナイザの違いによる分析結果を表示

トークナイザに "standard" を指定した場合と、"letter" を指定した場合で、分析結果を比較します。

"quick.brown_fox" の部分の分析結果が異なることが分かります。

コマンド

GET /library/_analyze

{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "THE quick.brown_FOx Jumped! $19.95 @ 3.0"
}

GET /library/_analyze
{
"tokenizer": "letter",
"filter": ["lowercase"],
"text": "THE quick.brown_FOx Jumped! $19.95 @ 3.0"
}

実行結果

# GET /library/_analyze

{
"tokens" : [
{
"token" : "the",
"start_offset" : 0,
"end_offset" : 3,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "quick.brown_fox",
"start_offset" : 4,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "jumped",
"start_offset" : 20,
"end_offset" : 26,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "19.95",
"start_offset" : 29,
"end_offset" : 34,
"type" : "<NUM>",
"position" : 3
},
{
"token" : "3.0",
"start_offset" : 37,
"end_offset" : 40,
"type" : "<NUM>",
"position" : 4
}
]
}

# GET /library/_analyze
{
"tokens" : [
{
"token" : "the",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 0
},
{
"token" : "quick",
"start_offset" : 4,
"end_offset" : 9,
"type" : "word",
"position" : 1
},
{
"token" : "brown",
"start_offset" : 10,
"end_offset" : 15,
"type" : "word",
"position" : 2
},
{
"token" : "fox",
"start_offset" : 16,
"end_offset" : 19,
"type" : "word",
"position" : 3
},
{
"token" : "jumped",
"start_offset" : 20,
"end_offset" : 26,
"type" : "word",
"position" : 4
}
]
}


 日本語を含むドキュメントの分析結果を表示

Elasticsearch は標準で日本語に対応したトークナイザがインストールされないため 、kuromoji プラグインをインストールしておく必要があります。

コマンド

GET /library/_analyze

{
"tokenizer": "kuromoji_tokenizer",
"text": "記者が汽車で帰社した"
}

実行結果

{

"tokens" : [
{
"token" : "記者",
"start_offset" : 0,
"end_offset" : 2,
"type" : "word",
"position" : 0
},
{
"token" : "が",
"start_offset" : 2,
"end_offset" : 3,
"type" : "word",
"position" : 1
},
{
"token" : "汽車",
"start_offset" : 3,
"end_offset" : 5,
"type" : "word",
"position" : 2
},
{
"token" : "で",
"start_offset" : 5,
"end_offset" : 6,
"type" : "word",
"position" : 3
},
{
"token" : "帰社",
"start_offset" : 6,
"end_offset" : 8,
"type" : "word",
"position" : 4
},
{
"token" : "し",
"start_offset" : 8,
"end_offset" : 9,
"type" : "word",
"position" : 5
},
{
"token" : "た",
"start_offset" : 9,
"end_offset" : 10,
"type" : "word",
"position" : 6
}
]
}