Elasticsearch Script度量聚集教程

Elasticsearch Script度量聚集教程

前面有兩篇博文詳細介紹了Elasticsearch的度量聚集。本文補充介紹腳本度量,實現使用腳本自定義邏輯提供度量輸出。

1. 語法介紹

這裏通過示例代碼來說明:

POST ledger/_search?size=0
{
    "aggs": {
        "profit": {
            "scripted_metric": {
                "init_script" : {
                    "id": "my_init_script"
                },
                "map_script" : {
                    "id": "my_map_script"
                },
                "combine_script" : {
                    "id": "my_combine_script"
                },
                "params": {
                    "field": "amount" 
                },
                "reduce_script" : {
                    "id": "my_reduce_script"
                }
            }
        }
    }
}

上面示例的腳本使用存儲腳本,也可以是內聯腳本。首先我們介紹腳本範圍,共分爲四個階段執行:

init_script
對所有集合的文檔最先執行的腳本,可選腳本,一般用於設置初始化狀態。

map_script
每個集合文檔執行一次。必須提供腳本。

combine_script
在集合文檔腳本執行之後,每個分片執行一次。必須提供腳本,用於從每個分片返回信息。

reduce_script
在所以分片返回結果後相應節點上執行一次。必須提供腳本。腳本能訪問combine_script的數組類型結果。

返回值類型

雖然任何有效腳本對象都可以在單個腳本中使用,但腳本必須僅返回或存儲在狀態對象中以下類型:

  • 基本類型
  • String類型
  • Map(包含key和這裏列舉的類型值)
  • Array(包括這裏列舉類型值元素)

params

可選,用於給init_script, map_script 、 combine_script 三個腳本傳遞參數變量。讓用戶可以控制聚集的行爲,在不同腳本之間存儲狀態。缺省值爲:"params" : {}

2. 示例

定義映射

PUT ledger
{
  "mappings": {
    "properties": {
      "type": {
        "type": "keyword"
      },
      "amount": {
        "type": "integer"
      }
    }
  }
}

導入示例數據

PUT /ledger/_bulk?refresh
{"index":{"_id":1}}
{"type": "sale","amount": 80}
{"index":{"_id":2}}
{"type": "cost","amount": 10}
{"index":{"_id":3}}
{"type": "cost","amount": 30}
{"index":{"_id":4}}
{"type": "sale","amount": 130}

實現需求

使用腳本聚集計算利潤:銷售額-成本。實現代碼:

POST ledger/_search?size=0
{
    "query" : {
        "match_all" : {}
    },
    "aggs": {
        "profit": {
            "scripted_metric": {
                "init_script" : "state.transactions = []", 
                "map_script" : "state.transactions.add(doc['type'].value == 'sale' ? doc['amount'].value : -1 * doc['amount'].value)",
                "combine_script" : "double profit = 0; for (t in state.transactions) { profit += t } return profit",
                "reduce_script" : "double profit = 0; for (a in states) { profit += a } return profit"
            }
        }
    }
}

返回結果:

{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "profit" : {
      "value" : 170.0
    }
  }
}

過程解釋

  1. 分片說明

這裏共四條數據:

{"index":{"_id":1}}
{"type": "sale","amount": 80}
{"index":{"_id":2}}
{"type": "cost","amount": 10}
{"index":{"_id":3}}
{"type": "cost","amount": 30}
{"index":{"_id":4}}
{"type": "sale","amount": 130}

假設第一、三兩天在Shard A,另外兩條在Shard B .下面我們詳細對每一步驟進行說明。

  1. 初始化(init_script)之前

state初始化爲一個空對象。

"state" : {}
  1. 初始化(init_script)之後

任何集合文檔執行之前每個分片執行一次,因此在每個分片上產生一個對象:

Shard A

"state" : {
    "transactions" : []
}

Shard B

"state" : {
    "transactions" : []
}


  1. map_script之後
    每個分片對每個文檔運行map_script腳本:

Shard A

"state" : {
    "transactions" : [ 80, -30 ]
}

Shard B

"state" : {
    "transactions" : [ -10, 130 ]
}

  1. combine_script 之後
    文檔收集之後,combine_script腳本在每個分片上執行,通過累加事務數組的值計算每個分片的利潤並傳給響應節點:

Shard A
50

Shard B
120

  1. reduce_script 之後

reduce_script腳本收到包括合併每個分片的結果的數組states

"states" : [
    50,
    120
]

最後通過累加聚集上面數組的值,生成最終返回的利潤結果:

{
    ...

    "aggregations": {
        "profit": {
            "value": 170
        }
   }
}

到此詳細的執行流程解釋完成。最後需要說明的是如果腳本聚集的父分組沒有收集到任何記錄,那麼響應會是null,因此處理腳本聚集時需要考慮這種情況。

3. 總結

本文介紹了Elasticsearch的腳本聚集,詳細介紹了其語法,並通過示例完整說明了其map-reduce的執行過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章