今天接續昨天挖的坑,來理解一下 Script Query 的靈魂: Painless 語言。
在 Elasticsearch 裡支援了利用 Script 對客製化的查詢語句 (custom expression) 進行文件查找,簡單來說就是把 「Document 拉到本地端進行邏輯運算」的動作,直接在 Elasticsearch 的 Runtime 中執行。對 Client 端的應用的好處來說,只要專注在「資料」本身。我想信對巨量資料處理上,在 Elasticsearch 中進行運作,有更大的效能優勢,只是我目前暫時無法探索那部分,留待日後研究。
在較舊版的 Elasticsearch 中,原本也支援 Groovy,Javascript 及 Python 等腳本語言,但 6.0 後都已經停止支援。目前 Elasticsearch 預設支援的腳本語言即為 Painless。
Painless 是以「安全性」與「效能」為前題,專為 Elasticsearch 設計的腳本語言。語法上與 Java 類似,但包含了額外的特性 (這等到寫 Code 時再來探索)。Painless 腳本經過編繹後,會運行在標準的 JVM runtime。雖然「效能」是一大特色,但 Script 使用上仍有效能上的考量 (官方文件),先挖個坑,不確定這場鐵人賽有沒有機會探索到。
Script 的 Query DSL 如下:
"script": {
"lang": "...", <== 預設是 painless
"source" | "id": "...", <== source: inline; id: stored script
"params": { ... }
}
通常,script query 會用在 filter 內文中。套路! 到 Kibana Dev Tools 來玩玩看。目標,搜尋股價在 10 元以上,100 元以下的個股。
GET /history-prices-daily/_search
{
"query" : {
"bool": {
"must": [
{"match": {
"date": "2020-09-23"
}}
],
"filter": {
"script": {
"script": {
"lang": "painless",
"source": "doc['close'] >= params.low_price && doc['close'] <= params.high_price",
"params": {
"low_price": 10,
"high_price": 100
}
}
}
}
}
}
}
登楞… 報錯:
"caused_by" : {
"type" : "class_cast_exception",
"reason" : "Cannot apply [>] operation to types [org.elasticsearch.index.fielddata.ScriptDocValues.Doubles] and [java.lang.Integer]."
}
根據文檔 ,這種類似 Map 的取值方式,應該是沒問題的,但事實証明,要把 Inline script 內的取值方式改成:
"doc['close'].**value** >= params.low_price && doc['close'].**value** <= params.high_price"
找時間要深入理解一下差異。結果找到 728 檔個股在這個股價範圍:
回到 Elasticsearch DSL 來試試:
s = Search(using=es, index="history-prices-daily") \
.query("match", date="2020-09-23") \
.filter( "script", script={
"lang": "painless",
"source": "doc['close'].value >= params.low_price && doc['close'].value <= params.high_price",
"params": {
"low_price": 10,
"high_price": 100
}
}
)
print(response['hits']['total']['value'])
// 728
得到一樣的結果。
明天就用 Script Query 讓選股程式再跑起來吧!