創建腳本
//語法:POST _script/名字
POST _script/calculate-discount
{
"script":{
"lang":"painless",
"source":"doc['price'].value * params.discount"
}
}
GET _script/calculate-discount
刪除腳本
DELETE _script/calculate-discount
引用腳本
GET product/_search
{
"script_fields":{
"discount_price":{
"script":{
//這里不需要再指定source,而是直接指定對應上面存儲了的腳本id名即可
"id":"calculate-discount",
"params":{
"discount":0.8
}
}
}
}
}
Painless簡介
Painless可以對文檔字段進行加工處理
更新或者刪除字段,處理數據聚合操作
Script Field:對返回的字段提前進行計算
Function Score:對文檔的分數進行處理
在Reindex API,Update By Query時,對數據進行處理
腳本編寫的語言,默認為painless。
腳本本身可以指定為內聯腳本的source或存儲腳本的id
應傳遞給腳本的任何命名參數
Painless關鍵字
if else while do for in continue break return new try catch throw this instanceof
通過Painless腳本訪問字段
Update ctx._source.field_name
Search&Aggregation doc['field_name']
可以將表達式腳本用於script_score、script_fields、排序腳本和數字聚合腳本,只需將lang參數設置為expression。
script腳本修改文檔
修改文檔時,通過ctx._source.fieldname來指定某個字段
POST pigg_test/_update/1
{
"script":"ctx._source.age += 1"
}
指定integer類型的age為一個新值
POST pigg_test/_update/1
{
"script":"ctx._source.age = 34"
}
另一種好用的方法
POST pigg_test/_update/1
{
"script":"ctx._source.age = params.value",
"params":{
"value":34
}
}
當要修改值時,只需修改params里的value值,不需要修改ctx._source.age = params.value這個腳本
指定數組添加一個值
POST pigg_test/_update/1
{
"script":"ctx._source.age(params.value)",
"params":{
"value":"王冬"
}
}
指定數組刪除某個值
POST pigg_test/_update/1
{
"script":{
"source":"ctx._source.remove(ctx._source.name.indexOf(params.value))",
"params":{
"value":"王冬"
}
}
}
先判斷是否存在,然后指定數組刪除某個值
POST pigg_test/_update/1
{
"script":{
"source":"if(ctx._source.name.indexOf(params.value) >=0) ctx._source.name.remove(ctx._source.name.indexOf(params.value))",
"params":{
"value":"王冬"
}
}
}
字段直接復制值
POST pigg_test/_update/1
{
"script":{
"source":"ctx._source.new_name = ctx._source.name"
}
}
但是這里得注意,這里也僅僅是復制值,不復制字段的mapping配置,如果不預先設置new_name的mapping配置,new_name還是會是ES的默認的text。
刪除一個字段,不修改mapping
POST pigg_test/_update/1
{
"script":"ctx._source.remove('new_name')"
}
獲取字段值方法
Doc_values是一個列式字段值存儲,默認情況下在除text字段外所有字段上啟用。
如果text類型,用doc['fieldname'].value會報錯
當text類型包含keyword子字段時,用doc['fieldname.keyword'].value就可以
doc['fieldname'].value params['_source']['my_field']
doc.fieldname.value
params._source.fieldname
理解doc['my_field'].value和params['_source']['my_field']之間的區別非常重要:
使用doc關鍵字: 將導致該字段的術語加載到內存(緩存)中, 這樣腳本的執行速度會更快, 但也會帶來更多的內存消耗. 另外, doc […]符號只允許簡單的值字段(不能從中返回JSON對象), 並且它只對非分析或基於單個術語的字段有意義.
使用params關鍵字: 每次使用時都必須加載和解析_source, 這是非常緩慢的.
建議: 使用doc關鍵字, 從文檔中訪問相關字段的值, 這種方式更加高效
GET pigg_test/_search
{
"script_fields":{
"sum_score":{
"script":{
"lang":"painless",
"source":"def sum = 0;sum = doc['chinese'].value + doc['match'].value + doc['english'].value; return sum;"
}
}
}
}
GET pigg_test/_search
{
"script_fields":{
"new_address":{
"script":{
"source":"'地址是:' + doc['address.keyword'].value"
}
}
}
}
如果text類型,用params._source.fieldname
GET pigg_test/_search
{
"script_fields":{
"new_address":{
"script":{
"source":"'地址是:' + params._source.address"
}
}
}
}
用size()或length來獲取數組的長度
GET pigg_test/_search
{
"query":{
"script":{
"script":"doc['name'].size() ==3"
}
}
}
GET pigg_test/_search
{
"query":{
"script":{
"script":"doc['name'].length == 3"
}
}
}
在聚合中使用script
GRT pigg_test/search
{
"size":10,
"aggs":{
"name_total_count":{
"sum":{
"script":"doc['name'].size()"
}
}
}
}
數字字段
doc['field_name'].value 字段的值,作為 double
doc['field_name'].empty 一個布爾值,指示該字段在文檔中是否沒有值
doc['field_name'].length 本文檔中的值的數量
doc['field_name'].min() 本文檔中字段的最小值
doc['field_name'].max() 本文檔中字段的最大值
doc['field_name'].median() 本文檔中字段的中值
doc['field_name'].avg() 本文檔中值的平均值
doc['field_name'].sum() 本文檔中的值的總和
當文檔完全缺少該字段時,默認情況下該值將被視為0. 您可以將其視為另一個值,
例如doc['myfield'].empty ? 100 : doc['myfield'].value
日期字段
doc['field_name'].date.centuryOfEra 世紀 (1-2920000)
doc['field_name'].date.dayOfMonth 日 (1-31),例如1一個月的第一天
doc['field_name'].date.dayOfWeek 星期幾 (1-7),例如1星期一
doc['field_name'].date.dayOfYear 一年中的某一天,例如11 月 1 日
doc['field_name'].date.era 時代:0對於公元前,1對於公元
doc['field_name'].date.hourOfDay 小時 (0-23)
doc['field_name'].date.millisOfDay 一天內的毫秒數 (0-86399999)
doc['field_name'].date.millisOfSecond 秒內的毫秒 (0-999)
doc['field_name'].date.minuteOfDay 一天中的分鍾 (0-1439)
doc['field_name'].date.minuteOfHour 小時內的分鍾 (0-59)
doc['field_name'].date.monthOfYear 一年中的月份 (1-12),例如1一月
doc['field_name'].date.secondOfDay 一天內的第二個 (0-86399)
doc['field_name'].date.secondOfMinute 一分鍾內的第二個 (0-59)
doc['field_name'].date.year 年(-292000000 - 292000000)
doc['field_name'].date.yearOfCentury 世紀內的年份 (1-100)
doc['field_name'].date.yearOfEra 時代內的年份(1-292000000)
顯示date字段 date0 和 date1 之間的年份差異
doc['date1'].date.year - doc['date0'].date.year
Geo_point字段
doc['field_name'].empty 一個布爾值,指示該字段在文檔中是否沒有值。
doc['field_name'].lat 地理點的緯度。
doc['field_name'].lon 地理點的經度。
以下示例以公里為單位計算距華盛頓特區的距離:
haversin(38.9072, 77.0369, doc['field_name'].lat, doc['field_name'].lon)
在這個例子中,坐標可以作為參數傳遞給腳本,例如基於用戶的地理位置。
腳本中的多行語句
ES是支持多行腳本編寫的:只需要source后面的語句的開頭和結尾都是三個引號,再兩對三引號之間寫下多行內容即可,語句和語句直接使用“;”結尾,和java差不多
POST product2/_update/1
{
"script":{
"lang":"painless",
"source":"""
ctx._source.name += params.name;
ctx._source.price -= 1
""",
"params":{
"name":"無線充電",
"price":"1"
}
}
}
script_score
允許用戶在檢索中靈活修改文檔score,來實現自己干預結果排名的目的,另外script score性能要高於function score
GET /_search
{
"query":{
"script_score":{
"query":{
"match":{"message":"elasticsearch"}
},
"script":{
"source":"doc['my-int'].value / 10"
}
}
}
}
query:(必須,查詢對象)用於返回文檔的查詢
script:(必須,腳本對象)用於計算query
查詢的最終相關性script_score不能為負數。為了支持某些優搜索化,Lucene要求分數為正數或0
min_score:(可選,浮點數)得分低於此浮點數的文檔將被排除在搜索結果之外
boost:(可選,浮點數)由產生的文檔分數script乘以boost產生最終文檔的分數。默認為1.0
在腳本中使用相關性分數:可以訪問_score表示文檔當前相關性分數的變量。
weight:允許您將分數乘以提供的weight,這有時可能是需要的,因為在特定查詢上設置的提升至會被標准化,而對於這個評分函數卻沒有。數值的類型是浮點型
random:生成從0到1但不包括1均勻分布的random_score分數,默認情況下,它會使用內部Lucene文檔ID作為隨機源,這非常有效,但不幸的是不可重現,因為文檔可能會通過合並重新編號。
field_value_factor:該功能允許您使用文檔中的字段來影響分數。它類似於使用script_score函數,但是,他避免了編寫腳本的開銷。如果用於多值字段,則僅在計算中使用該字段的第一個值。
假設您有一個使用數字my-int字段索引的文檔,並希望使用該字段影響文檔的分數
GET /_search
{
"query":{
"function_score":{
"field_value_factor":{
"field":"my-int",
"factor":1.2,
"modifier":"sqrt",
"missing":1
}
}
}
}
這將轉化為一下評分公式:sqrt(1.2 * doc['ny-int'].value)
該功能有許多選項field_value_factor:
field 要從文檔中提取的字段。
factor 與字段值相乘的可選因子。默認為1
modifier 應用於字段的修飾符,可以使一下之一:none,log,log1p,log2p,ln,ln1p,ln2p,square,sqrt或reciprocal,默認為none。
衰減函數:使用一個函數對文檔進行評分,該函數根據文檔的數字字段值與用戶給定原點的距離而衰減。這類似於范圍查詢,但邊緣平滑而不是框。
要對具有數字字段的查詢使用距離評分,用戶必須為每個字段定義一個origin和一個scale。origin需要定義計算距離的中心點,以及定義scale衰減率,衰減函數指定為
"DECAY_FUNCTION":{ 應該是linear,exp或者gauss之一。
"FIELD_NAME":{ 指定的字段必須是數字,日期或地理點字段
"origin":"11,12",
"scale":"2km",
"offset":"0km",
"decay":0.33
}
}
gauss:正常衰減
exp:指數衰減
linear:線性衰減
預定義函數
Saturation
saturation(value,k) = value/(k + value)
"script":{
"source":"saturation(doc['my-int'].value,1)"
}
Sigmoid
si
script_fields
您可以使用改script_fields參數來檢索每個命中的腳本評估(基於不同的字段)。
GET /_search
{
"query":{
"match_all":{}
},
"script_fields":{
"test1":{
"script":{
"lang":"painless",
"source":"doc['price'].value * 2"
}
},
"test2":{
"script":{
"lang":"painless",
"source":"doc['price'].value * params.factor",
"params":{
"factor":2.0
}
}
}
}
}
腳本字段可以處理未存儲的字段(price在上述情況下),並允許腳本字段還可以訪問實際_source文檔並使用params['_source']
GET /_search
{
"query":{
"match_all":{}
},
"script_fields":{
"test1":{
"script":"params['_source']['message']"
}
}