知識分享-ELK介紹 (2)
=====
# Agenda
1. ElasticSearch 介紹
2. Elasticsearch Index 設計
3. Elasticsearch 資料查找
4. Elasticsearch 資料統計

# 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"
}
}
}
}
}
}
}
}
}
}
}
```