#ElasticSearchの検索クエリに関して
目的
ElasticSearchの基本的な検索のやり方の説明と記述の仕方を説明する。
使用環境
- Amazon Elasticsearch Service
- Elasticsearch6.4
- Kibana6.4
##用語説明
###Index
索引。
このindexに検索対称のドキュメントを格納。Indexは複数もつことが可能。
例:先頭にuser_price_index_がつくIndexが1日ごとに作られる
- user_price_index_20190501
- user_price_index_20190502
- user_price_index_20190503
- user_price_index_20190504
###Type
リレーショナルデータベースでいえばテーブルに相当。
バージョン6以降は廃止される。
###Document
格納されたドキュメント。
リレーショナルデータベースでいえば1レコードに相当。
##サンプルログ
ユーザーがとあるお店で、商品を購入したと想定
東スーパー、西コンビニ、南商店、北デパートと4つのお店が存在し、以下の商品を購入
商品名 | 単価 |
---|---|
キノコの山 | 120円 |
コアラのマーチ | 92円 |
杉のこ村 | 111円 |
たけのこの里 | 140円 |
ハーゲンダッツ | 1000円 |
ログはjson形式で登録されており以下のような構造になっている。
ユーザーがお店に行ったら作られるログ
{"user_id": 52, "kind_name": "アクセス", "place": "北デパート", "@timestamp": "2019-06-13T19:55:23+09:00:00"}
ユーザーが購入したら作られるログ
{"user_id": 43, "kind_name": "購入", "price": 140, "place": "南商店", "total_price": 280, "total_price_with_tax": 302.4, "product": "たけのこの里", "p_value": 2, "@timestamp": "2019-06-29T05:42:17+09:00:00"}
ログは1日ごとに作成されるuser_price_index_yyymmddという名前のIndexにdocumentとして記録される。1
##検索の基本
ElasticSeachは レコードの処理(検索、作成、更新、削除処理)が可能なREST APIが用意されているのでそれを利用する。
Kibana のDevToolを利用する場合は以下のように記述して、実行すれば検索可能。
検索パラメータははjson形式で記述する。
GET user_price_index_*/_search
{
"size":3,
"query": {
....
}
}
sizeで取得件数を指定し、query内に検索条件を記述する
##検索クエリ
自分がよく使う検索クエリとそのサンプルを紹介。
###term
指定したフィールドを完全一致検索する。
"term":{
"フィールド":検索値
}
文字列のフィールドを完全一致検索する場合はフィールド名に*.keyword*をつける。
"term":{
"フィールド.keyword":"検索値"
}
例:productが「杉のこ村」のdocumentを検索する場合の検索クエリ
GET user_price_index_*/_search
{
"size":3,
"query": {
"term":{
"product.keyword":"杉のこ村"
}
}
}
検索結果
{
"took": 60,
"timed_out": false,
"_shards": {
"total": 145,
"successful": 145,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 938,
"max_score": 2.9618306,
"hits": [
{
"_index": "user_price_index_20190608",
"_type": "_doc",
"_id": "BBmGxGoBzDMFYAVDLKj9",
"_score": 2.9618306,
"_source": {
"user_id": 60,
"kind_name": "購入",
"price": 111,
"place": "西コンビニ",
"total_price": 444,
"total_price_with_tax": 479.52,
"product": "杉のこ村",
"p_value": 4,
"@timestamp": "2019-06-08T06:45:22+09:00:00"
}
},
{
"_index": "user_price_index_20190602",
"_type": "_doc",
"_id": "qxmIxGoBzDMFYAVDcLq0",
"_score": 2.7725887,
"_source": {
"user_id": 1,
"kind_name": "購入",
"price": 111,
"place": "西コンビニ",
"total_price": 444,
"total_price_with_tax": 479.52,
"product": "杉のこ村",
"p_value": 4,
"@timestamp": "2019-06-02T07:30:48+09:00:00"
}
},
{
"_index": "user_price_index_20190604",
"_type": "_doc",
"_id": "ShmHxGoBzDMFYAVDqrUM",
"_score": 2.7725887,
"_source": {
"user_id": 56,
"kind_name": "購入",
"price": 111,
"place": "南商店",
"total_price": 555,
"total_price_with_tax": 599.4,
"product": "杉のこ村",
"p_value": 5,
"@timestamp": "2019-06-04T20:21:35+09:00:00"
}
}
]
}
}
termの複数条件版。
"terms":{
"フィールド":[検索値1, 検索値2,...]
}
文字列のフィールドを完全一致検索する場合はフィールド名に*.keyword*をつける。
"terms":{
"フィールド.keyword":["検索値1","検索値2",...]
}
例:productが「杉のこ村」と「きのこの山」のdocumentを検索する場合の検索クエリ
GET user_price_index_*/_search
{
"size":3,
"query": {
"terms":{
"product.keyword":["杉のこ村","キノコの山"]
}
}
}
検索結果
{
"took": 18,
"timed_out": false,
"_shards": {
"total": 145,
"successful": 145,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1935,
"max_score": 1,
"hits": [
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "0BmGxGoBzDMFYAVDLKX5",
"_score": 1,
"_source": {
"user_id": 23,
"kind_name": "購入",
"price": 111,
"place": "東スーパー",
"total_price": 222,
"total_price_with_tax": 239.76,
"product": "杉のこ村",
"p_value": 2,
"@timestamp": "2019-06-01T19:19:35+09:00:00"
}
},
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "_BmGxGoBzDMFYAVDLKj9",
"_score": 1,
"_source": {
"user_id": 57,
"kind_name": "購入",
"price": 111,
"place": "南商店",
"total_price": 333,
"total_price_with_tax": 359.64,
"product": "杉のこ村",
"p_value": 3,
"@timestamp": "2019-06-01T14:38:21+09:00:00"
}
},
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "KxmHxGoBzDMFYAVDqq0K",
"_score": 1,
"_source": {
"user_id": 34,
"kind_name": "購入",
"price": 120,
"place": "南商店",
"total_price": 120,
"total_price_with_tax": 129.6,
"product": "キノコの山",
"p_value": 1,
"@timestamp": "2019-06-01T21:02:55+09:00:00"
}
}
]
}
}
###range
検索条件を範囲指定できるクエリ。
"range":{
"フィールド名":{
"gt": 範囲開始(境界含まない),
"gte": 範囲開始(境界含む),
"lt": 範囲終了(境界含まない),
"lte": 範囲終了(境界含む),
}
検索条件が時間の場合
"range":{
"フィールド名":{
"time_zone": "タイムゾーン",
"gt": 範囲開始(境界含まない),
"gte": 範囲開始(境界含む),
"lt": 範囲終了(境界含まない),
"lte": 範囲終了(境界含む),
}
例:total_priceが500以上、1000未満のdocumentを検索する場合の検索クエリ
GET user_price_index_*/_search
{
"size":3,
"query": {
"range":{
"total_price":{
"gte": 500,
"lt": 1000
}
}
}
}
検索結果
{
"took": 79,
"timed_out": false,
"_shards": {
"total": 145,
"successful": 145,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 817,
"max_score": 1,
"hits": [
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "GRmHxGoBzDMFYAVDqrAL",
"_score": 1,
"_source": {
"user_id": 81,
"kind_name": "購入",
"price": 140,
"place": "南商店",
"total_price": 700,
"total_price_with_tax": 756,
"product": "たけのこの里",
"p_value": 5,
"@timestamp": "2019-06-01T06:34:09+09:00:00"
}
},
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "oRmHxGoBzDMFYAVDqrUM",
"_score": 1,
"_source": {
"user_id": 59,
"kind_name": "購入",
"price": 140,
"place": "東スーパー",
"total_price": 700,
"total_price_with_tax": 756,
"product": "たけのこの里",
"p_value": 5,
"@timestamp": "2019-06-01T22:19:17+09:00:00"
}
},
{
"_index": "user_price_index_20190601",
"_type": "_doc",
"_id": "yBmIxGoBzDMFYAVDcLez",
"_score": 1,
"_source": {
"user_id": 57,
"kind_name": "購入",
"price": 140,
"place": "南商店",
"total_price": 560,
"total_price_with_tax": 604.8,
"product": "たけのこの里",
"p_value": 4,
"@timestamp": "2019-06-01T23:42:33+09:00:00"
}
}
]
}
}
例:2019-06-28に記録されたログを検索する場合のクエリ
GET user_price_index_*/_search
{
"size":3,
"query": {
"range":{
"@timestamp":{
"time_zone": "Asia/Tokyo",
"gte": "2019-06-28T00:00:00",
"lt": "2019-06-29T00:00:00"
}
}
}
}
検索結果
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 145,
"successful": 145,
"skipped": 140,
"failed": 0
},
"hits": {
"total": 217,
"max_score": 1,
"hits": [
{
"_index": "user_price_index_20190628",
"_type": "_doc",
"_id": "mhmGxGoBzDMFYAVDLKLc",
"_score": 1,
"_source": {
"user_id": 83,
"kind_name": "アクセス",
"place": "北デパート",
"@timestamp": "2019-06-28T06:41:56+09:00:00"
}
},
{
"_index": "user_price_index_20190628",
"_type": "_doc",
"_id": "JBmGxGoBzDMFYAVDLKPc",
"_score": 1,
"_source": {
"user_id": 1,
"kind_name": "アクセス",
"place": "西コンビニ",
"@timestamp": "2019-06-28T04:16:37+09:00:00"
}
},
{
"_index": "user_price_index_20190628",
"_type": "_doc",
"_id": "pBmGxGoBzDMFYAVDLKPc",
"_score": 1,
"_source": {
"user_id": 57,
"kind_name": "購入",
"price": 120,
"place": "西コンビニ",
"total_price": 360,
"total_price_with_tax": 388.8,
"product": "キノコの山",
"p_value": 3,
"@timestamp": "2019-06-28T09:32:34+09:00:00"
}
}
]
}
}
#ElasticSearchシリーズ
- ElasticSearchの検索クエリに関して(基礎編)
- [ElaticSearchの検索クエリに関して(応用編)] (https://qiita.com/horankey_jet_city/private/67bb73b42014cfb99f27)
- [ElasticSearchの集約クエリに関して(基礎編)] (https://qiita.com/horankey_jet_city/private/94eda5046a8fe1febe45)
- ElasticSearchの集約クエリに関して(応用?編)
-
yyymmddには、ログを記録した日付が入る。例えば、2019-06-01に作成されたログなら20190601 ↩