创建脚本
//语法: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']"
}
}
