---
# System prepended metadata

title: 知識分享-ELK介紹 (2)

---

知識分享-ELK介紹 (2)
=====

# Agenda

1. ElasticSearch 介紹
2. Elasticsearch Index 設計
3. Elasticsearch 資料查找
4. Elasticsearch 資料統計

![](https://i.imgur.com/IlLwUys.png)


# ElasticSearch 介紹

1. [Lucene](https://zh.wikipedia.org/wiki/Lucene) 全文檢索引擎
2. 使用 Rest API
3. 使用 JSON 作為文檔資料序列化格式
4. 文件導向資料庫
    - 可以存儲整個物件(Object)或文檔(Document)，提供文檔資料的處理、索引、搜索、排序、過濾。
5. Near RealTime (NRT) 微小延遲接近即時 (normally 1s)
6. 分散式架構



<iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fpermalink.php%3Fstory_fbid%3D1385281961640196%26id%3D998741260294270&show_text=true&width=500" width="500" height="712" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>

# Elasticsearch Index 設計

## Dynamic mapping and Explicit mappings


1. 動態 Mapping
    - Elasticsearch僅通過索引文檔即可自動添加新欄位。

:::info
使用[Dynamic templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html)來定義自定義映射，這些自定義映射將基於匹配條件動態添加欄位。
:::


2. 明確 Mapping
    - 在建立index的時候創建映射類型和欄位映射，或通過mapping API添加映射類型和欄位到一個已經存在的index中。


## 欄位資料型態

>參考 : 
>[mapping-types](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html)

### String datatypes
1. Text
    - 用於索引全文值的欄位，分析過程允許 Elasticsearch 搜索每個全文欄位中的單個單詞。
    - Text 的欄位不用於排序，很少用於聚合。
2. Keyword
    - 用於索引結構化內容的欄位，它們通常用於過濾、排序和聚合。
    - Keyword 欄位只能按其確切值進行搜索。


### Numeric datatypes

- Long: 帶符號64位元整數 (-2^64^ ~ 2^64^-1)
- Integer: 帶符號32位元整數 (-2^32^ ~ 2^32^-1)
- Short:帶符號16位元整數 (-2^16^ ~ 2^16^-1)
- Byte:帶符號8位元整數 (-2^8^ ~ 2^8^-1)
- Double: 雙精度64位元浮點數
- Float:精度32位元浮點數
- Half_float:半精度16位元浮點數
- Scaled_float:會透過 scaling factor把浮點數字變成整數


```json=
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "number_of_bytes": {
        "type": "integer"
      },
      "time_in_seconds": {
        "type": "float"
      },
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      }
    }
  }
}
```


### Date datatype

JSON沒有日期數據類型，因此Elasticsearch中的日期可以是：
- 包含格式化日期的字符串 "2015-01-01"或"2015/01/01 12:10:30"。
- 一個自紀元以來毫秒數的數字。
- 一個自紀元以來秒數的數字。

```json=
PUT my-index-000001/_doc/1
{ "date": "2015-01-01" } 

PUT my-index-000001/_doc/2
{ "date": "2015-01-01T12:10:30Z" } 

PUT my-index-000001/_doc/3
{ "date": 1420070400001 } 
```


日期格式可以自定義，但是如果未指定，則預設使用：
:::info
"strict_date_optional_time||epoch_millis"
:::



### Boolean datatype

布林欄位接受JSON true和false值，但也可以接受被解釋為true或false的字符串。
- False values: false , "false"
- True valus: true, "true"


### Binary datatype

二進制類型接受二進制值作為Base64編碼的字符串。 默認情況下不存儲該欄位，並且不可搜索。



```json=
PUT my-index
{
  "mappings" : {
      "properties" : {
        "rawdata" : {
          "type" : "binary"
        }
      }
  }
}

POST my-index2/_doc/
{
  "rawdata" : "aGVsbG93b3JsZA=="
}

```



### Range datatype


Range 型態支持
- Integer_range:一個帶符號32位元Range (-2^32^ ~ 2^32^-1)
- Float_range: 一個精度32位元浮點數Range
- Long_range: 一個帶符號64位元Range(-2^64^ ~ 2^64^-1)
- Dobule_range: 一個雙精度64位元浮點數Range
- Date_range:自系統紀元以來經過的無符號64位整數毫秒。
- Ip_range: 支持IPv4或IPv6（或混合）地址的一系列ip值


**建立index 與欄位定義**
```json=
PUT range_index
{
  "mappings": {
    "properties": {
      "expected_attendees": {
        "type": "integer_range"
      },
      "time_frame": {
        "type": "date_range", 
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}
```

**新增資料**
```json=
PUT range_index/_doc/1?refresh
{
  "expected_attendees" : { 
    "gte" : 10,
    "lt" : 20
  },
  "time_frame" : {
    "gte" : "2015-10-31 12:00:00", 
    "lte" : "2015-11-01"
  }
}
```

**範圍搜尋**
```json=
GET range_index/_search
{
  "query" : {
    "range" : {
      "time_frame" : { 
        "gte" : "2015-10-31",
        "lte" : "2015-11-01",
        "relation" : "within" 
      }
    }
  }
}
```


### Array datatype


在Elasticsearch中，沒有專用的Array類型。
預設情況下，任何欄位都可以包含零個或多個值，而 Array 中的所有值必須具有相同的資料類型。
例如：
- 字串陣列: ["one","two"]
- 數值陣列: [1,2]
- 陣列的陣列: [1, [2,3]] 相當於 [1,2,3]
- 物件陣列: [{"name":"Mary","age":12}, {"name":"John","age":10}]



### Object datatype

- JSON文檔本質上是分層的：文檔可能包含內部物件

```json=
PUT my-index-000001/_doc/1
{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}
```



## Index Lifecycle

> 參考: 
>[喬叔教 Elastic - 10 - 管理 Index 的 Best Practices (2/7) - 三溫暖架構 - Hot Warm Cold Architecture](https://ithelp.ithome.com.tw/articles/10243650)


1. Hot Node 
    - 因為會處理 Indexing 的請求，所以 JVM heap 會有一定比例拿來處理 Indexing。
2. Warm Node 
    - 不處理 Indexing 所以只會有一半 JVM heap 拿來處理 query request，剩下的會來給 Lucene 用作暫存。
3. Cold Node 
    - 不會處理 Indexing，而且針對 query request 所產生的 transient cache 也會一用完就盡快的釋放，減少 heap 的使用。
3. Delete
    - 長時間不被使用的Index，可安全被刪除。



### 建立 Lifecycle Policy

**國際卡監控使用 housekeeping policy**

```json=
PUT /_ilm/policy/housekeeping_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
        }
      },
      "warm": {
        "min_age": "30h",
        "actions": {
        }
      },
      "cold": {
        "min_age": "14d",
        "actions": {
        }
      },
      "delete": {
        "min_age": "35d",
        "actions": {
          "delete": {
          }
        }
      }
    }
  }
}

GET /_ilm/policy/housekeeping_policy?pretty=true
```

**alert policy**

當超過 100mb 或 200000筆資料進行滾動，替換至下一個index
```json=
PUT /_ilm/policy/alert_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "100mb",
            "max_docs": 200000
          }
        }
      },
      "delete": {
        "min_age": "7d",
        "actions": {
          "delete": {
          }
        }
      }
    }
  }
}

GET /_ilm/policy/alert_policy?pretty=true
```

### 建立 Index Template

> 參考 : 
> [喬叔教 Elastic - 06 - Index 建立前你該知道的 (3/5) - ES 的超前佈署 - Index Template](https://ithelp.ithome.com.tw/articles/10239736?sc=hot)

**以國際卡監控 Index Template 為例**

transaction_template : 
1. index_patterns
    - 套用該 Template 的 index patterns
2. priority
    -  Index Template 的優先順序,數字愈大愈優先
3. version
    - 自定義版本號
4. template
    - 可以包含 Aliases, Mappings, Index Settings 的設定
5. number_of_shards
    - 將Index細分為多個分片分散儲存在多個節點。
    - 提供水平拆分/縮放資料、分片分佈及平行化操作，以提高資料操作性能及吞吐量。
6. number_of_replicas
    - 為防止Index大量處理資料時，造成分片及節點異常後資料的損失。
    - 提供一個或多個副本的索引分片，確保分片及節點發生故障時提供高可用性，並且支持在副本上並行搜索。
7. index.lifecycle.name
    - 套用 Lifecycle Policy



```json=
PUT /_index_template/transaction_template
{
  "index_patterns": [
    "transaction_message-*",
    "application_status_tree-*",
    "application_log-*",
    "batch_log-*"
  ],
  "priority": 100,
  "version": "00001",
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.lifecycle.name": "housekeeping_policy"
    }
  }
}

GET /_index_template?name=transaction_template&pretty=true
```

alert_template:
- index.lifecycle.rollover_alias : 替 index 取別名

```json=
PUT /_index_template/alert_template
{
  "index_patterns": [
    "alert-*"
  ],
  "priority": 100,
  "version": "00001",
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "alert_policy",
      "index.lifecycle.rollover_alias": "alert"
    }
  }
}
GET /_index_template?name=alert_template&pretty=true

```


# Elasticsearch 資料查找


## Query DSL

**Query DSL (Domain Specific Language) 提供基於JSON來定義查詢。**

由兩種類型的子句組成：
- Leaf query clauses (簡單查詢)
  - 葉查詢子句查找特定欄位中的特定值，例如 match, term 或 range 查詢。這些查詢可以單獨使用。
- Compound query clauses (複雜查詢)
  - 複合查詢子句包裝其他葉查詢或複合查詢，用於以邏輯方式組合多個查詢(例如bool)



## 分詞


把全文字轉換成一系列單詞（term/token）的過程叫做分詞


```json=
GET _analyze
{
  "analyzer": "standard",
  "text": "this is a test"
}
```

:::info
"this is a test" 進行分詞 => ["this","is","a","test"]
:::



分詞系統使用特殊符號進行分隔
標點符號可能是單詞的一部分取決於它的位置：

```json=
GET /_analyze?tokenizer=standard

You're my 'favorite'.
```

:::info
["You're","my","favorite"]
:::


>參考 : 
>[ElasticSearch 分詞器，瞭解一下](https://www.gushiciku.cn/pl/pYl8/zh-tw)
>[CKIP 中文斷詞](https://ckip.iis.sinica.edu.tw/resource)


## 資料欄位型別

### text 和 keyword 區別

- text 進行分詞
- keyword 不進行分詞

```json=
GET /my-index/

{
  "my-index" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "message" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}
```

- 欄位message 為 text
- 欄位message.keyword 為 keyword


## Search API



### Match And Term query

Match
- 提供的文本，數字，日期或布爾值匹配的文檔。
- 匹配之前對提供的文本進行分析。
- 該match查詢是用於執行全文搜索的標準查詢，其中包括模糊匹配的選項。


Term
- 提供的欄位中包含確切詞語的文檔。
- 您可以使用term查詢根據精確的值（例如價格，產品ID或用戶名）查找文檔。

:::info
避免term對text欄位使用查詢。
默認情況下，Elasticsearch更改text欄位的值，作為analysis的一部分,這會使查找text欄位值的精確匹配變得困難。
要搜索text欄位值，請改用match查詢。
:::


#### match和term的區別


- match 進行分詞
- term  不進行分詞


範例

```json=
#建立資料
POST my-index/_doc/
{
  "message" : "this is a test"
}
```

分詞結果["this","is","a","test"]



match 匹配成功
    
```json=
#match 查詢
GET /my-index/_search
{
  "query": {
    "match": {
      "message":"this is a test"
    }
  }
}
```


- match分詞後["this","is","a","test"],任一條件匹配即可



term 匹配失敗

```json=
#term 查詢
GET /my-index/_search
{
  "query": {
    "term": {
      "message":"this is a test"
    }
  }
}
```

- term不進行分詞，將["this is a test"]視為一個分詞條件
- 若要使term匹配,則要使用message.keyword




1. term查詢keyword欄位,需要完全匹配才可以
2. term查詢text欄位,term查詢條件必須是text欄位分詞後
3. match查詢text欄位,只要match的分詞結果和text的分詞結果有相同就匹配
4. match查詢keyword欄位,ES使用Term Analyzer，與term查詢一致



### match_all

搜尋全部文檔
```json=
GET /my-index/_search
{
  "query": {
    "match_all": {}
  }
}

```



### multi_match

(match query)允許多欄位查詢
```json=
GET /my-index/_search
{
  "query": {
    "multi_match": {
      "query":"this is a",
      "fields": ["message","name"]
    }
  }
}
```



### match_phrase

match_phrase : 欄位分詞中都包含，而且順序必須相同，而且必須都是連續的
 
```json=
 GET /my-index/_search
{
  "query": {
    "match_phrase": {
      "message":"this is a test"
    }
  }
}
```



### match_phrase_prefix
 
match_phrase_prefix 和match_phrase類似，只不過match_phrase_prefix 支援最後一個term字首比對
 
```json=
GET /my-index/_search
{
  "query": {
    "match_phrase_prefix": {
      "message":"this is a te"
    }
  }
}
```



### range query
```json=
GET /transaction_message-*/_search
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-1d/d",
        "lt": "now/d"
      }
    }
  }
}
```



```json=
GET /transaction_message-*/_search
{
  "query": {
    "range": {
      "timestamp": {
        "time_zone": "+08:00",
        "gte": "2020-05-01T00:00:00", 
        "lte": "now"
      }
    }
  }
}
```



### Bool query

- Bool 查詢可以把任一個簡單查詢組合在一起，使用must、should、must_not 關鍵字處理。


|Bool query|邏輯運算式|
|--|--|
|must|AND|
|should|OR|
|must_not|NOT|



```json=
GET /my-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "should": [
              {
                "match":{"message":"this"}
              },
              {
                "match":{"message":"is"}
              }
            ],
            "must_not": [
              {
                "match":{"message":"test"}
              }
            ]
          }
        },
        {
          "bool": {
            "must": [
              {
                "match":{"message":"shark"}
              }
            ]
          }
        }
      ]
    }
  }  
}
```



## Query and Filter

- 查詢子句的使用行為取決於它是在查詢、還是在篩選使用：
1. Query DSL:
    - 查詢除了確定文檔是否匹配外，並且計算一個_score，表示文檔相對於其他文檔的匹配程度。
2. Filter DSL:
    - 查詢文檔是否與此查詢子句匹配，並不計算任何分數，因此篩選上下文主要用於篩選結構化資料，而Elasticsearch會自動緩存常用的篩選器，以加快性能。



```json=
GET /transaction_message-*/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term":{"message.mti.keyword":"0800"}
        }
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-1d/d",
              "lt": "now/d"
            }
          }
        }
      ]
    }
  }  
}
```



### From/Size
- 在請求體中的查詢欄位允許使用查詢DSL定義一個查詢。透過From和Size參數控制分頁，size參數允許你配置返回結果的最大數量。

注意: from+size不能超過index.max_result_window選項，默認值是10000



```json=
#第1頁,取10筆
GET /transaction_message-*/_search
{
  "from": 1, 
  "size": 10, 
  "query": {
    "bool": {
      "must": [
        {
          "term":{"message.mti.keyword":"0800"}
        }
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-1d/d",
              "lt": "now/d"
            }
          }
        }
      ]
    }
  }  
}
```


### Sort

允許添加一個或者多個排序，每個排序都可以指定排序規則。使用欄位名的排序是定義在欄位層面的，使用_score表示按得分排序，使用_doc表示按索引時間的順序排序。

```json=
GET /transaction_message-*/_search
{
  "from": 1, 
  "size": 10, 
  "sort": [
    {"message.mti.keyword": "desc"},
    "_score"
  ], 
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-1d/d",
              "lt": "now/d"
            }
          }
        }
      ]
    }
  }  
}
```


### Sort Values

排序值也作為返回結果的一部分返回

|Sort|以order選項控制|
|--|--|
|asc|升序|
|desc|降序|

|參數| 說明|
|--|--|
|min| 數組中最小的值作為排序值
|max| 數組中最大的值作為排序值
|sum| 數組中所有元素的總和作為排序值
|avg| 數組中所有元素的平均值作為排序值
|median| 使用所有值的中位數作為排序值



```json=
POST price/_doc/
{
  "name": "bbb",
  "price" : [1,10,51,63,20,100]
}

GET /price/_search
{
 "sort": [
   {
     "price": {
       "order": "desc",
       "mode": "min"
     }
   }
 ]
}
```



### Query string query

查詢使用語法運算符（例如AND 、 NOT）來解析和拆分提供的查詢字符串。

[說明](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html)




```json=
GET tb_inc_internal_sync-*/_search
{
  "size": 1,
  "query": {
    "query_string":{
      "analyze_wildcard":true,
      "query":"tran_code:05 AND card_plan:M AND setl_flag:9"
      
    }
  }
}
```


#### Wildcards 通配符

使用 ? 代表1個字元
使用 * 代表0到多 個字元



#### Regular expression 正規表示式

在反斜線內使用正規表示式
name:/joh?n(ath[oa]n)/



#### Fuzziness 模糊查詢
可以搜尋相近但不完全不正確的詞
查找最大兩個變化的所有術語，其中變化是單個字符的插入，刪除或替換，或者兩個相鄰字符的轉置。

```json=
quikc~ brwn~ foks~
```



預設的編輯距離是2，但是編輯距離1應該足以捕捉所有人類拼寫錯誤的80％。可以指定為：

```json=
quikc~1
```



### Ranges
可以為日期，數字或字串指定範圍。
包含範圍用方括號[min TO max]
排除範圍用花括號{min TO max}



```json=
2012年所有天數：
date：[2012-01-01 TO 2012-12-31]

數字1..5

count：[1至5]
於alpha和omega之間的tag，但不包括alpha和omega：

tag：{alpha TO omega}

10以上的數字
count：[10 TO *]

2012年之前的日期
date：{* TO 2012-01-01}

數字1到5但不包含5

count:[1 TO 5}

```


## SQL access



### SQL REST API

使用 SQL REST API 進行查詢

Kibana:
```json=
POST /_sql?format=json
{
  "query": """SELECT * FROM "tb_inc_internal_sync-*" ORDER BY tran_code DESC LIMIT 1"""
}
```

<!-- .slide: style="font-size: 26px;" -->



### SQL Translate API

將API 語法 轉換為Elasticsearch 原生語法
```json=
POST /_sql/translate
{
  "query": """SELECT * FROM "tb_inc_internal_sync-*" where tran_code.keyword='05'  ORDER BY tran_code DESC LIMIT 1""",
  "fetch_size": 10
}
```


# Elasticsearch 資料統計


>參考 : 
>[search-aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html)



Elasticsearch將聚合分為三類：
1. Bucket Aggregations
    - 根據field的值、範圍或其他條件將documents分組為桶。
1. Metrics Aggregations
    - 從field的值計算度量，如總和或平均值。
1. Pipeline Aggregations
    - 匯總其他聚合ouput獲取輸入。



## Terms aggregation

>參考 : 
>[search-aggregations-bucket-terms-aggregation](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html)

- 針對某個 field 的值進行分組，field 有幾種值就分成幾組
- terms 桶在進行分組時，會爲此 field 中的每種值創建一個新的桶



### 範例1


```json=
GET tb_inc_internal_sync-*/_search
{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_tran_code": {
            "terms": {
                "field": "tran_code.keyword",
                "min_doc_count": 1,
                "size": 10,
                "order": {"_key": "asc"}
            }
        }
    }
}
```
參數:
- min_doc_count 限制最低數量
- size 顯示 bucket 中多少筆數據，預設是 10
- order 預設是照doc_count排序
- script 可以使用腳本來組合欄位再統計數量
- include/exclude 可以藉由這兩個參數filter設定的條件



處理結果

```json=
  "aggregations" : {
    "my_tran_code" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "05",
          "doc_count" : 245
        },
        {
          "key" : "06",
          "doc_count" : 31
        }
      ]
    }
  }
```



### 範例2

將 terms 桶搭配度量指標（avg、min、max、sum…）一起使用


```json=
GET tb_inc_internal_sync-*/_search
{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_tran_code": {
            "terms": {
                "field": "tran_code.keyword",
                "min_doc_count": 0
            },
            "aggs": { 
                "my_avg_amt": {
                    "avg": {
                        "field": "src_amt"
                    }
                },
                "my_sum_amt": {
                    "sum": {
                        "field": "src_amt"
                    }
                },
                "my_min_amt": {
                    "min": {
                        "field": "src_amt"
                    }
                }
            }
        }
    }
}
```



```json=
  "aggregations" : {
    "my_tran_code" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "05",
          "doc_count" : 245,
          "my_min_amt" : {
            "value" : 500.0
          },
          "my_avg_amt" : {
            "value" : 2024105.7142857143
          },
          "my_sum_amt" : {
            "value" : 4.959059E8
          }
        },
        {
          "key" : "06",
          "doc_count" : 31,
          "my_min_amt" : {
            "value" : 500.0
          },
          "my_avg_amt" : {
            "value" : 3990.3225806451615
          },
          "my_sum_amt" : {
            "value" : 123700.0
          }
        }
      ]
    }
  }
```


##  Filter aggregation


### 範例

```json=
GET tb_inc_internal_sync-*/_search
{
  "size": 0,
  "aggs": {
    "my_avg_amt": { "avg": { "field": "src_amt" } },
    "my_filter": {
      "filter": { "term": { "tran_code": "05" } },
      "aggs": {
        "avg_price": { "avg": { "field": "src_amt" } }
      }
    }
  }
}
```


結果
```json=
  "aggregations" : {
    "my_filter" : {
      "doc_count" : 245,
      "avg_price" : {
        "value" : 2024105.7142857143
      }
    },
    "my_avg_amt" : {
      "value" : 1797208.6956521738
    }
  }
```


使用 filters aggregation 做多重過濾
要使用多個過濾器對文檔進行分組，請使用 filters聚合。這比使用多個filter聚合要快。

```json=
POST /sales/_search?size=0&filter_path=aggregations
{
  "aggs": {
    "f": {
      "filters": {
        "filters": {
          "hats": { "term": { "type": "hat" } },
          "t_shirts": { "term": { "type": "t-shirt" } }
        }
      },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

```


取代
```json=
POST /sales/_search?size=0&filter_path=aggregations
{
  "aggs": {
    "hats": {
      "filter": { "term": { "type": "hat" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    },
    "t_shirts": {
      "filter": { "term": { "type": "t-shirt" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}
```


## Range aggregation

在聚合過程中，將從每個存儲區範圍中檢查從每個文檔中提取的值，並“存儲”相關/匹配的文檔。
請注意，此聚合包括from值，但不包括to每個範圍的值。



### 範例
```json=
GET tb_inc_internal_sync-*/_search
{
  "size": 0,
  "aggs": {
    "amt_ranges": {
      "range": {
        "field": "src_amt",
        "ranges": [
          { "to": 10000 },
          { "from": 10000, "to": 20000 },
          { "from": 20000 }
        ]
      }
    }
  }
}
```

結果
```json=
  "aggregations" : {
    "amt_ranges" : {
      "buckets" : [
        {
          "key" : "*-10000.0",
          "to" : 10000.0,
          "doc_count" : 94
        },
        {
          "key" : "10000.0-20000.0",
          "from" : 10000.0,
          "to" : 20000.0,
          "doc_count" : 46
        },
        {
          "key" : "20000.0-*",
          "from" : 20000.0,
          "doc_count" : 136
        }
      ]
    }
  }
```



## 範例 (國際卡日報統計)

**filter 使用 時間戳記 及 message.mti 過濾**
**Group by role,mti,procCode 並統計 error ,success,system_err,timeout 筆數**

```json=
GET transaction_message-*/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "@timestamp": {
              "time_zone": "+08:00",
              "gte": "2021-06-10T00:00:00",
              "lt": "2021-06-11T00:00:00"
            }
          }
        },
        {
          "query_string": {
            "analyze_wildcard": true,
            "query": "!(message.mti:0800)"
          }
        }
      ]
    }
  },
  "aggs": {
    "role": {
      "terms": {
        "field": "message.role.keyword",
        "size": 10000
      },
      "aggs": {
        "mti": {
          "terms": {
            "field": "message.mti.keyword",
            "size": 10000
          },
          "aggs": {
            "pcode": {
              "terms": {
                "field": "message.procCode.keyword",
                "size": 10000
              },
              "aggs": {
                "error": {
                  "filter": {
                    "query_string": {
                      "analyze_wildcard": true,
                      "query": "resperr:1"
                    }
                  }
                },
                "success": {
                  "filter": {
                    "query_string": {
                      "analyze_wildcard": true,
                      "query": "resperr:0"
                    }
                  }
                },
                "system_err": {
                  "filter": {
                    "query_string": {
                      "analyze_wildcard": true,
                      "query": "resperr:1 AND message.respCode:(03|15|19|21|30|68|74|79|80|88|89|91|92|96|98|I1|I2|I3|I6|I7|I8|I9|IA)"
                    }
                  }
                },
                "timeout": {
                  "filter": {
                    "query_string": {
                      "analyze_wildcard": true,
                      "query": "resperr:1 AND message.isTimeout:true"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

```