the industrial

ブログと言うより自分のためのメモ以外の何モノでもないです。でも読んでくださってありがとうございます。

Elasticsearch入門してます(3日目) IndexとMapping周りを今一度

というわけで、あけおめです。

ブログであけおめなどとボヤくのは何年ぶりかしら。

2016年のお仕事も始まったわけなのだけど、初日の通勤電車(1時間)では Elasticsearch の学習が3日坊主にならないように、オフィシャルサイトのドキュメントを眺めておったり。

このエントリーは、仕事で1年弱ほどElasticsearchを触っていたものの、”なんとなく、やったら出来た”という感じでElasticsearchを使っていたところ、そうするとやっぱりなんとなくのクオリティーしか出来ていない気がしていて、これは非常に良くないと。

さらに、今後Aggregation周りの機能をガリガリと使うことになりそうで、その辺を含め”なんとなく、やってたら出来た”から”狙って作ってやった”という形に持って行きたいと思い、まずは基礎から足場を固め、Aggregatoin周りを攻めるべく精進していこうと。

まあまあ、そんな目的でこのブログのエントリーにメモとして残しておくというもの。

最近ものぐさな人間だったりして、かつ、もともと気分屋なところもあるので、エントリーによっては丁寧に書いたり書かなかったりとだらだらやってはいるものの、もしも偶然こんなブログに来てしまった方におかれましては得られるが一つでもアレばと。ただ、先述の通りあくまでもメモレベルのものなので、実際はオフィシャルサイトなどをご参考にされたほうが100倍良いと思いマサール。

さてさて、前回は簡単なクエリーを〜なんて言っておきながら、読み返したらGETメソッドでデータを取得しているだけで、クエリー書いてないじゃんという自分への突っ込みがあったので、Aggregationに行く前に改めてそこら辺をやろうかなと。

Indexを作りなおした

やりたいAggregationは日付けの期間指定(Date Range Aggregationとか)などを想定しているので、そこら辺を意識したMappingに変えてみた。

Index index-a にはTypeが type-atype-b の2つ存在し、Documentとして nameattributeperiodcreatedDateupdatedDate を保持。

period は内部に startDateendDate を保持している形。

mapping_index-a.json

{
  "mappings": {
    "type-a": {
      "dynamic": "strict",
      "_source": {
        "enabled": true
      },
      "_all": {
        "enabled": false
      },
      "_ttl": {
        "enabled": false
      },
      "properties": {
        "name": {
          "type": "string"
        },
        "attribute": {
          "type": "string",
          "index": "not_analyzed"
        },
        "period": {
          "type": "object",
          "properties": {
            "startDate": {
              "type": "date",
              "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
            },
            "endDate": {
              "type": "date",
              "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
            }
          }
        },
        "createdDate": {
          "type": "date",
          "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
        },
        "updatedDate": {
          "type": "date",
          "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
        }
      }
    },
    "type-b": {
      "dynamic": "strict",
      "_source": {
        "enabled": true
      },
      "_all": {
        "enabled": false
      },
      "_ttl": {
        "enabled": false
      },
      "properties": {
        "name": {
          "type": "string"
        },
        "attribute": {
          "type": "string",
          "index": "not_analyzed"
        },
        "period": {
          "type": "object",
          "properties": {
            "startDate": {
              "type": "date",
              "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
            },
            "endDate": {
              "type": "date",
              "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
            }
          }
        },
        "createdDate": {
          "type": "date",
          "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
        },
        "updatedDate": {
          "type": "date",
          "format": "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
        }
      }
    }
  }
}

Indexの登録

前回と同じ。

$ curl -XPOST http://localhost:9200/index-a?pretty=true --data @mapping_index-a.json
{
  "acknowledged" : true
}

Indexの登録内容確認

Indexが登録されているかどうかはaliasの確認でわかる。

$ curl -XGET localhost:9200/_aliases?pretty=true
{
  "index-a" : {
    "aliases" : { }
  }
}

Index名がわかっているならば、こうするとmappingまで詳細に確認できる。

もちろん、mappingsの直下にくるのがTypeですな。

Get Index

$ curl -XGET localhost:9200/index-a?pretty
{
  "index-a" : {
    "aliases" : { },
    "mappings" : {
      "type-b" : {
        "dynamic" : "strict",
        "_all" : {
          "enabled" : false
        },
        "_ttl" : {
          "enabled" : false
        },
        "properties" : {
          "attribute" : {
            "type" : "string",
            "index": "not_analyzed"
          },
          "createdDate" : {
            "type" : "date",
            "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
          },
          "name" : {
            "type" : "string"
          },
          "period" : {
            "properties" : {
              "endDate" : {
                "type" : "date",
                "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
              },
              "startDate" : {
                "type" : "date",
                "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
              }
            }
          },
          "updatedDate" : {
            "type" : "date",
            "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
          }
        }
      },
      "type-a" : {
        "dynamic" : "strict",
        "_all" : {
          "enabled" : false
        },
        "_ttl" : {
          "enabled" : false
        },
        "properties" : {
          "attribute" : {
            "type" : "string",
            "index": "not_analyzed"
          },
          "createdDate" : {
            "type" : "date",
            "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
          },
          "name" : {
            "type" : "string"
          },
          "period" : {
            "properties" : {
              "endDate" : {
                "type" : "date",
                "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
              },
              "startDate" : {
                "type" : "date",
                "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
              }
            }
          },
          "updatedDate" : {
            "type" : "date",
            "format" : "YYYY-MM-dd'T'HH:mm:ss.SSSZ"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1451899718047",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "Fq91gHD-Q96Z84WqOh0Fkg",
        "version" : {
          "created" : "2010199"
        }
      }
    },
    "warmers" : { }
  }
}

Indexに関しては他にもいろいろ触りたいAPIがあるのだけど、今回はこんなところで。

Indices APIs

テストデータを登録

作ったテストデータはこちら

↑は type-a 用で、 type-b については ATTR の5桁目を1万の代にして登録しており、合計で2万件くらいデータをぐわーっと入れる。

$ curl -XPOST localhost:9200/index-a/_bulk --data-binary '@bulk_index-a.json'

Search API

Search APIs

Elasticsearchの検索方法はいろいろあるみたいで、まずはちょー基本っぽい、リクエストパラメータで検索を行うSearch APIを試してみる。

条件なしはこんな感じ。

$ curl -XGET localhost:9200/index-a/_search?pretty

実行すると、こう帰ってくる。

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 20000,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00005",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00005", "attribute" : "ATTR00005", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00007",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00007", "attribute" : "ATTR00007", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00010",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00010", "attribute" : "ATTR00010", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00011",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00011", "attribute" : "ATTR00011", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00018",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00018", "attribute" : "ATTR00018", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00022",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00022", "attribute" : "ATTR00002", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00026",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00026", "attribute" : "ATTR00006", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00030",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00030", "attribute" : "ATTR00010", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00033",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00033", "attribute" : "ATTR00013", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00035",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00035", "attribute" : "ATTR00015", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    } ]
  }
}

10件しか帰ってこないのは、Elasticsearchのデフォルト設定のおかげ。sizefrom でページングの様な検索もできる。

さらに条件指定した場合。

ドメイン以下をシングルコートで囲まないと、prettyが死んだ)

$ curl -XGET 'localhost:9200/index-a/_search?q=name:NAME00001&size=3&from0&pretty'

結果。

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 8.1767645,
    "hits" : [ {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00001",
      "_score" : 8.1767645,
      "_source":{ "name" : "NAME00001", "attribute" : "ATTR00001", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-b",
      "_id" : "00001",
      "_score" : 8.1767645,
      "_source":{ "name" : "NAME00001", "attribute" : "ATTR10001", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    } ]
  }
}

それから、_id指定でデータを取得する場合はこんな感じ。(わかりやすく size=50 とか指定している)

$ curl -XGET 'localhost:9200/index-a/type-a/00001?pretty&size=50&from=0'
{
  "_index" : "index-a",
  "_type" : "type-a",
  "_id" : "00001",
  "_version" : 1,
  "found" : true,
  "_source":{ "name" : "NAME00001", "attribute" : "ATTR00001", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
}

まあ、この辺はあえてやらなくともサイト見てやってれば基本的になんとでもなりますかねえ。

URI Search

Query DSL

これが割りととっつきにくい。まず英語力がないので、ドキュメントに書いてあるような長い英文ガーw

Query DSL

match query

いろいろな検索方法があるみたいなのだけど、たとえばMach Query。

全件検索はこんな感じで、Search APIJSON形式で条件を書くイメージ。

$ curl -XGET localhost:9200/index-a/type-a/_search?pretty -d '{
  "size": 5,
  "from" : 0,
  "query" : {
    "match_all" : {}
  }
}'

結果。

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 10000,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00005",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00005", "attribute" : "ATTR00005", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00007",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00007", "attribute" : "ATTR00007", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00010",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00010", "attribute" : "ATTR00010", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00011",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00011", "attribute" : "ATTR00011", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00018",
      "_score" : 1.0,
      "_source":{ "name" : "NAME00018", "attribute" : "ATTR00018", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    } ]
  }
}

はてさて、肝心の条件指定方法はと言うと...

$ curl -XGET localhost:9200/index-a/_search?pretty -d '{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "NAME00002" } }
      ]
    }
  }
}'

とかってぶん投げると、

{
  "took" : 34,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 8.1767645,
    "hits" : [ {
      "_index" : "index-a",
      "_type" : "type-a",
      "_id" : "00002",
      "_score" : 8.1767645,
      "_source":{ "name" : "NAME00002", "attribute" : "ATTR00002", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-b",
      "_id" : "00002",
      "_score" : 8.1767645,
      "_source":{ "name" : "NAME00002", "attribute" : "ATTR10002", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    } ]
  }
}

こんな感じで帰ってくる。

term query

Term Query

あまり良くわかっていないのだけど、文字列に対して検索するようなものですかなあ。

"type": "string","index":"not_analyzed"と指定したDocumentに対して、完全一致でマッチするような事がオフィシャルサイトに。

今回作成したIndexの"type": "string"としているDocumentでは、nameは特に指定なく、attributeは上記の通りとしているので...

$ $ curl -XGET localhost:9200/index-a/_search?pretty -d '{
  "from":490,"size":2, "query": {
    "term": {
      "attribute": "ATTR10001"
    }
  }
}'

↑これが

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 500,
    "max_score" : 4.9059286,
    "hits" : [ {
      "_index" : "index-a",
      "_type" : "type-b",
      "_id" : "09201",
      "_score" : 4.5730176,
      "_source":{ "name" : "NAME09201", "attribute" : "ATTR10001", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    }, {
      "_index" : "index-a",
      "_type" : "type-b",
      "_id" : "09321",
      "_score" : 4.5730176,
      "_source":{ "name" : "NAME09321", "attribute" : "ATTR10001", "period" : { "startDate": "2015-01-01T15:00:00.000Z", "endDate": "2015-01-02T14:59:59.999Z" } , "createdDate" : "2015-01-01T15:00:00.000Z", "updatedDate" : "2015-01-01T15:00:00.000Z" }
    } ]
  }
}

↑こうなって、

$ curl -XGET localhost:9200/index-a/_search?pretty -d '{
  "query": {
    "term": {
      "name": "NAME00001"
    }
  }
}'

↑これは

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

↑こうなると。

うーーーむ。ココらへんは検索とは別の話なので、一旦スルーするか。でもElasticsearchの大事なところの様な気もするのだけど、英語力ぐぬぬ

ちょっと長くなってしまったので、今日はこんなものか...。

やばい、検索するだけでめっちゃ沢山覚えることありそうで、なかなかAggregationにイケナイ!

引き続きガンバリマス。

omiend.hatenablog.jp

omiend.hatenablog.jp

omiend.hatenablog.jp