読者です 読者をやめる 読者になる 読者になる

修行@ホーチミン

ホーチミン長期出張の日記です

Elasticsearch 超入門

前書き

 

APIに勉強がてらElasticsearchを導入してみたいなと考え、まずElasticsearch勉強しようと思うも、

Elasticearchに関する超初心者用の日本語ドキュメントはかなり少なそうで..。

Elasticsearch Reference [2.4] | Elastic

やはり上記の公式ドキュメントは英語ですが分かりやすいので、ノート代わりにシンプルに内容をまとめます。

認識の違いがあったらごめんなさい。

 

セットアップ

 

■ Javaのバージョン確認

  • 最低でもJava7以上である必要があります。
java -version
echo $JAVA_HOME

■ Download

  • Unix環境を前提に

適当にディレクトリ移動して、下記コマンドでダウンロード

curl -L -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.1/elasticsearch-2.4.1.tar.gz

下記コマンドで解凍

tar -xvf elasticsearch-2.4.1.tar.gz

■ 起動

解凍したらelasticsearch-2.4.1のようなディレクトリがあるはずなので、配下の./binに移動

cd elasticsearch-2.4.1/bin

下記のコマンドで起動します。

./elasticsearch

コマンド実行するとこんな感じで最終的にstarted が表示されます。

[2016-09-30 11:05:37,078][INFO ][node                     ] [Paibo] started

 

基本操作

 

■ Index生成

curl -XPUT 'localhost:9200/customer'
  • ポート番号はデフォルトで9200
  • customerというIndexを作成(MySQLでいうところのdatebaseでしょうか)
  • ?prettyをつけるとレスポンスのJSONが整形されて出力されます

?prettyなし

curl -XPUT 'localhost:9200/customer'
{"acknowledged":true}

?prettyあり

curl -XPUT 'localhost:9200/customer?pretty'
{
  "acknowledged" : true
}

Index確認

customerが追加されてます。

curl 'localhost:9200/_cat/indices?v'
health status index    pri rep docs.count docs.deleted store.size pri.store.size 
yellow open   customer   5   1          0            0       260b           260b 

 

■ Type生成

curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
  "name": "John Doe"
}'
  • externalというtypeを作成(MySQLでいうところのtableですね)
  • /1 でidを指定してレコード追加しています。

Response

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}

type確認

id=1のtypeを確認します。

curl -XGET 'localhost:9200/customer/external/1?pretty''

Response

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "name" : "John Doe"
  }
}

追加方法について

id=1で、{"name": "John Doe"}を作成した後

 curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
  "name": "John Doe"
}'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}

id=1で、{"name": "Jane Doe"}を生成しようとすると失敗します。

curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
  "name": "Jane Doe"
}'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 2,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : false
}

idを指定しないと、適当なidが付与されます。

curl -XPOST 'localhost:9200/customer/external?pretty' -d '
> {
>   "name": "Jane Doe"
> }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "AVd5vZRmkCj9N-SGg2rZ",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}

 

■ Index削除

curl -XDELETE 'localhost:9200/customer?pretty'
{
  "acknowledged" : true
}

削除されていることを確認

curl 'localhost:9200/_cat/indices?v'
health status index pri rep docs.count docs.deleted store.size pri.store.size 

customerが消えています。

 

■ DocumentのUPDATE

Documentはレコードという認識でよさそうです。

このようにカラム"age"を追加したような、UPDATEも可能です。

curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '
{
  "doc": { "name": "Jane Doe", "age": 20 }
}'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}

確認

curl -XGET 'localhost:9200/customer/external/1?pretty'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "found" : true,
  "_source" : {
    "name" : "Jane Doe",
    "age" : 20
  }
}

 

■ DocumentのDELETE

id=1のDocumentを削除する例

curl -XDELETE 'localhost:9200/customer/external/1?pretty'
{
  "found" : true,
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 4,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}

 

チュートリアル

 

準備

 

■ サンプルデータをダウンロード

https://github.com/bly2k/files/blob/master/accounts.zip?raw=true

上記URLより、サンプルデータをダウンロードした後「accounts.json」というファイルを展開。

accounts.json

下記のようなデータで構成されてます

{
    "account_number": 0,
    "balance": 16623,
    "firstname": "Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 Columbus Place",
    "employer": "Euron",
    "email": "bradshawmckenzie@euron.com",
    "city": "Hobucken",
    "state": "CO"
}

■ Elasticsearchへ取り込み

「accounts.json」ファイルのあるディレクトリへ移動し、下記コマンドで取り込み

curl -XPOST 'localhost:9200/bank/account/_bulk?pretty' --data-binary "@accounts.json"
確認
curl 'localhost:9200/_cat/indices?v'
health status index pri rep docs.count docs.deleted store.size pri.store.size 
yellow open   bank    5   1       1000            0    447.2kb        447.2kb 
  • bankというIndexが作成されている。
  • docs.countよりDocumentの数が1000であることが確認できる。

 

SearchAPI

 

■ クエリの種類

SearchAPIのクエリの発行方法は二種類

全件検索を例とすると、下記のようになる。

クエリ(1)
curl 'localhost:9200/bank/_search?q=*&pretty'
クエリ(2)
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} }
}'

■ JSON使用例

size
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "size": 1
}'
  • "size":で検索結果の件数を指定可能。例では1件。
  • デフォルトは10
from
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}'
  • "from":で検索開始位置の指定が可能。
  • 開始番号が0なので、例では11番目から10件となる。
  • デフォルトは 0。
sort
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "sort": { "account_number": { "order": "desc" } }
}'
  • 例では"account_number"のカラムを降順に10件表示となる。
_source
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}'
  • クエリの対象を指定する。
  • 例では"account_number""balance"が表示される。

■ queryの使用例

"account_number"が20
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match": { "account_number": 20 } }
}'
"address"に"mill"が含まれる
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match": { "address": "mill" } }
}'
"address"に"mill"または"lane"が含まれる
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match": { "address": "mill lane" } }
}'
"address"に"mill lane"が含まれる
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_phrase": { "address": "mill lane" } }
}'

match⇒match_phraseにすることで、"mill lane"を含むものと指定できます。

■ Bool Query

must
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}'
  • "address"に"mill"と"lane"のどちらも含まれるDocumentが選択される。
should
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}'
  • "address"に"mill"もしくは"lane"の含まれるDocumentが選択される。
must_not
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}'
  • "address"に"mill"と"lane"どちらも含まないDocumentが選択される。
mustとmust_notの組み合わせ
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "gender": "M" } }
      ]
    }
  }
}'
  • "age"が"40"であるが"gender"が"M"ではないDocumentが選択される。
filter
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}'
  • 文字通り検索結果にfilterが可能
  • 例の場合全件検索結果に、"balance"が20000以上、300000以下というfilterをかけてDocumentが選択される。
range 意味
gte Greater-than or equal to
gt Greater-than
lte Less-than or equal to
lt Less-than

■Aggregations(グループ的な意味)

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state"
      }
    }
  }
}'
  • "size": 0としているのは検索結果の表示は必要ではないため。
  • "terms":はGROUP BYを意味します。
  • "field":"state"でGROUP BYとしてます
  • GROUP BYされた"state"とその件数を、件数の降順に表示します
  • SQLで表すと下記のようになる。

SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC

range 意味
min 最小値
max 最大値
sum 合計
avg 平均値
terms GROUP BY
range 範囲指定でGROUP BY
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}'
  • "range":"field": "age"が[20-30,30-40,40-50]という条件でGROUP BY。
  • "terms":"field": "gender"をさらにGROUP BY。
  • "avg":でGROUP BYの結果から"field": "balance"のavg(平均値)を出力

 

 

とりあえずElasticsearchの基礎になるところを、ざっとまとめてみましたが足りてない部分はしれっと追記していきます。