文章目錄
- 一. 腳本是什么?
- 1. `lang`(腳本語言)
- 2. `source`(腳本代碼)
- 3. `params`(參數)
- 4. `id`(存儲腳本的標識符)
- 5. `stored`(是否為存儲腳本)
- 6. `script` 的上下文(Context)
- 7.完整示例
- 內聯腳本(Inline Script)
- 存儲腳本(Stored Script)
- 8.字段總結表
- 二. 腳本能做什么?
- 1. 腳本查詢(Script Query)
- 2. 腳本聚合(Script Aggregation)
- 3. 更新文檔(Update By Script)
- 4. 腳本排序(Script Sort)
- 5. 腳本字段(Script Field)
- 6. 索引時腳本(Ingest Pipeline)
- 7. 腳本評分(Script Score Query)
- 8. 數組操作(修改數組字段)
- 關鍵點總結
- 三. 為什么很多操作可以用腳本完成?
- 1 靈活性
- 2 避免冗余存儲
- 3 批量操作效率
- 4 擴展性
- 四. 腳本類型與執行方式
- 1 腳本語言
- 2 執行上下文
- 3 腳本存儲方式
- 五. 安全與性能注意事項
- 1 安全性
- 2 性能優化
- 六. 典型應用場景
一. 腳本是什么?
腳本是 ES 中一段可執行的代碼片段,通常用于在查詢或數據處理過程中動態計算值、修改文檔、實現復雜邏輯。ES 支持多種腳本語言,但默認推薦使用 Painless(ES 專門為性能和安全性設計的腳本語言)。
在 Elasticsearch 中,腳本(Script)的組成通常包括以下幾個核心字段,每個字段的作用和含義如下:
1. lang
(腳本語言)
-
含義:指定腳本使用的編程語言。
-
默認值:
painless
(Elasticsearch 推薦的高性能腳本語言)。 -
其他選項:
expression
(簡單表達式)、groovy
(舊版本支持,需謹慎啟用)。 -
示例:
"script": {"lang": "painless","source": "..." }
2. source
(腳本代碼)
-
含義:腳本的具體邏輯代碼,用指定的
lang
語言編寫。 -
作用:定義動態計算邏輯,例如字段操作、條件判斷、數學運算等。
-
示例:
"script": {"source": "doc['price'].value * params.discount" }
3. params
(參數)
-
含義:傳遞給腳本的外部參數,用于動態調整腳本行為。
-
作用:避免硬編碼,提高腳本復用性。
-
示例:
"script": {"source": "doc['price'].value * params.discount","params": { "discount": 0.8 } }
4. id
(存儲腳本的標識符)
-
含義:引用預先存儲在 Elasticsearch 中的腳本(通過
_scripts
API 存儲)。 -
作用:避免重復編寫相同腳本,提升性能(預編譯)。
-
示例:
"script": {"id": "calculate_profit" }
5. stored
(是否為存儲腳本)
-
含義:標識腳本是否已存儲(通常與
id
配合使用)。 -
示例:
"script": {"stored": true,"id": "my_script" }
6. script
的上下文(Context)
雖然不是字段,但腳本的執行上下文決定了其行為,例如:
- 查詢上下文:在
query
或bool
查詢中過濾或評分。 - 聚合上下文:在
aggs
中生成計算字段。 - 更新上下文:在
update
或update_by_query
中修改文檔字段。
7.完整示例
內聯腳本(Inline Script)
{"query": {"script": {"script": {"lang": "painless","source": "doc['price'].value * params.discount > 100","params": { "discount": 0.8 }}}}
}
- 字段解釋:
lang
: 使用 Painless 語言。source
: 計算price
字段乘以折扣后是否大于 100。params
: 傳遞折扣參數0.8
。
存儲腳本(Stored Script)
-
存儲腳本:
POST _scripts/calculate_profit {"script": {"lang": "painless","source": "doc['revenue'].value - doc['cost'].value"} }
-
調用存儲腳本:
{"aggs": {"total_profit": {"sum": {"script": {"id": "calculate_profit"}}}} }
8.字段總結表
字段 | 含義 | 類型 | 是否必需 | 默認值 |
---|---|---|---|---|
lang | 腳本語言 | String | 否 | painless |
source | 腳本代碼邏輯 | String | 是(或 id ) | - |
params | 傳遞給腳本的參數 | Object | 否 | {} |
id | 存儲腳本的唯一標識符 | String | 否 | - |
stored | 是否引用存儲腳本 | Boolean | 否 | false |
二. 腳本能做什么?
腳本幾乎可以覆蓋 ES 的所有核心操作,常見用途包括:
以下是一些 Elasticsearch 腳本的常見使用場景示例及其詳細說明:
1. 腳本查詢(Script Query)
場景:根據動態條件過濾文檔(如價格乘以折扣后大于 100)。
{"query": {"bool": {"must": {"script": {"script": {"lang": "painless","source": "doc['price'].value * params.discount > params.threshold","params": {"discount": 0.8,"threshold": 100}}}}}}
}
說明:
- 使用
params
傳遞折扣率和閾值,避免硬編碼。 doc['price']
直接訪問字段的數值類型(比_source
更高效)。
2. 腳本聚合(Script Aggregation)
場景:按利潤(收入 - 成本)分組統計。
{"aggs": {"profit_groups": {"terms": {"script": {"lang": "painless","source": "doc['revenue'].value - doc['cost'].value"},"size": 10}}}
}
說明:
- 通過腳本動態計算利潤字段,無需預先存儲該字段。
- 使用
terms
聚合對利潤分桶統計。
3. 更新文檔(Update By Script)
場景:為符合條件的文檔增加瀏覽量(views
字段 +1)。
POST /index/_update_by_query
{"script": {"source": "ctx._source.views += params.increment","params": { "increment": 1 }},"query": { "term": { "user": "alice" } }
}
說明:
ctx._source
訪問文檔的原始內容。- 通過
params.increment
參數化增量值,避免硬編碼。
4. 腳本排序(Script Sort)
場景:根據動態權重(如點擊量乘以系數)排序。
{"sort": {"_script": {"type": "number","script": {"source": "doc['clicks'].value * params.weight","params": { "weight": 1.5 }},"order": "desc"}}
}
說明:
- 使用
_script
自定義排序邏輯。 type
指定排序值的類型(如number
或string
)。
5. 腳本字段(Script Field)
場景:在查詢結果中添加一個動態計算的字段(如價格等級)。
{"query": { "match_all": {} },"script_fields": {"price_level": {"script": {"source": """if (doc['price'].value > 1000) {return 'high';} else {return 'low';}"""}}}
}
說明:
script_fields
在返回結果中添加一個臨時字段price_level
。- 使用條件判斷(
if-else
)動態分類。
6. 索引時腳本(Ingest Pipeline)
場景:在數據寫入時自動添加時間戳字段。
PUT _ingest/pipeline/add_timestamp
{"description": "Add timestamp at ingest time","processors": [{"script": {"source": "ctx.timestamp = new Date().getTime()"}}]
}
說明:
- 通過 Ingest Pipeline 在索引時執行腳本。
ctx
表示當前文檔的上下文,直接修改字段。
7. 腳本評分(Script Score Query)
場景:根據自定義邏輯影響文檔相關性評分(如按點擊量加分)。
{"query": {"script_score": {"query": { "match_all": {} },"script": {"source": "Math.log(1 + doc['clicks'].value) * params.boost","params": { "boost": 2 }}}}
}
說明:
script_score
結合數學函數(如對數)動態調整評分。params.boost
控制權重參數。
8. 數組操作(修改數組字段)
場景:向文檔的 tags
數組中添加新標簽。
POST /index/_update/1
{"script": {"source": "ctx._source.tags.add(params.new_tag)","params": { "new_tag": "popular" }}
}
說明:
ctx._source.tags.add()
直接操作數組字段。- 使用
params
傳遞動態參數。
關鍵點總結
-
語法規范:
- 使用
doc['field']
訪問數值型字段(高效)。 - 使用
ctx._source
訪問文檔原始內容(靈活但較慢)。
- 使用
-
參數化:
- 通過
params
傳遞動態值,避免腳本注入風險。
- 通過
-
存儲腳本:
- 頻繁使用的腳本應存儲(
POST _scripts/<id>
)以提升性能。
- 頻繁使用的腳本應存儲(
-
安全限制:
- 默認啟用 Painless 沙箱,禁止文件/網絡操作。
三. 為什么很多操作可以用腳本完成?
1 靈活性
- 動態邏輯:無需提前定義字段或索引結構,直接通過腳本實現復雜計算。
- 條件處理:根據實時參數或文檔內容動態調整行為(如
if-else
判斷)。
2 避免冗余存儲
- 按需計算:無需預先存儲所有可能的派生字段(如利潤、折扣價),在查詢時通過腳本實時計算。
3 批量操作效率
- 原子性更新:通過腳本直接修改文檔字段,避免先獲取再更新的網絡開銷(如
update_by_query
)。
4 擴展性
- 自定義評分:在搜索時通過腳本影響文檔的相關性得分(如結合地理位置、用戶行為)。
四. 腳本類型與執行方式
1 腳本語言
- Painless(默認,安全且高性能)
- Expression(簡單數學表達式)
- 其他(如 Groovy、JavaScript,但需謹慎啟用)
2 執行上下文
- 查詢時腳本:在
query
或aggs
中實時計算。 - 索引時腳本:在文檔寫入時通過
ingest pipeline
處理數據。 - 更新時腳本:在
update
或update_by_query
中修改文檔。
3 腳本存儲方式
-
內聯腳本:直接嵌入到請求中(簡單但重復使用時效率低)。
-
存儲腳本:將腳本保存在 ES 中,通過 ID 調用(復用性強,提升性能):
POST _scripts/calculate_profit {"script": {"lang": "painless","source": "doc['revenue'].value - doc['cost'].value"} }
五. 安全與性能注意事項
1 安全性
-
ES 默認啟用腳本沙箱機制,限制敏感操作(如文件讀寫、網絡訪問)。
-
可通過
elasticsearch.yml
配置禁用或限制腳本類型:script.allowed_types: inline script.allowed_contexts: search, update
2 性能優化
- 避免復雜計算:腳本在查詢時逐文檔執行,復雜邏輯可能導致延遲。
- 使用存儲腳本:預編譯存儲的腳本減少解析開銷。
- 限制字段訪問:通過
doc['field']
(快速)而非_source
(慢)獲取字段值。
六. 典型應用場景
- 電商搜索:根據用戶位置動態調整運費或顯示本地化價格。
- 日志分析:實時解析日志字段并生成統計信息。
- 風控系統:根據用戶行為實時計算風險評分。
- 數據清洗:在索引前標準化或豐富數據(如拆分字段、添加時間戳)。