1. script 简述
elasticsearch的script使用比较灵活而且广泛,有必要先了解和学习一下
1. script的类型和作用
script 可以让你自定义的返回一些field,也可以让你自定义score的计算。
在es中可以使用的script的类型有三种,就像是三种编程语言一样
- painless: 这个是最推荐的一种方式
- expression: lucene expression
- mustache: 这个一般用来定义search_template使用
- java
painless 是效率比较好,也比较灵活的一种。后面介绍script的功能的时候也主要以这个为主进行介绍。
2. script使用的方式
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}
source 部分是script的代码或者是 使用id,调用存储的script的id。
2. script的使用场景
在不同的使用场景下script能够使用的doc的field是不相同的。
1. update scripts
在update, update-by-query, reindex API可以通过ctx前缀获取到
- ctx._source : doc的_source 字段
- ctx.op : 当前操作,index/delete/noop
- ctx._index 等: doc的meta-fields,有些是只读的
2. search 和agg 中使用script
script fields 会在每个搜索命中的doc执行一次,其他在search和agg中使用的脚本将针对可能与search或agg匹配的每个文档执行一次,所有的普通字段值可以通过下面方式获取
- doc-values : 获取方式为doc[‘cost_price’].value,只有有doc_value的字段才可以这样使用,text字段理论上不能这样用
- the _source field : params._source.first_name 这里的params前缀必须要有
- stored fields : params._fields[‘first_name’].value
同时还可以在一些特定的查询中获取比如score等字段
3. search中获取doc的打分数据
获取和修改文档的_score字段只能够在 function_score query, script-based sorting, 或者 aggregations 当中
具体的使用方式有不太相同,在function_score中使用 script_score属性,在script-based sorting 中使用_script 属性
script语法,点号“.”表示获取字段,中括号表示获取数组“[]”,对应的样例就是
params._source.first_name
params._fields[‘first_name’].value
3. script灵活使用的一些理解
script可以简单的理解像java一样
常见的数据类型有
- 字符串:对应的方法有.length() 方法
- 数组[]: 对应的属性有.length 对应的方法有.add() .size() 可以试试看看对应于field的value是不是可以添加元素,可能是根据现有field value的类型来决定的
- 正则:/aa/.split(str) 这个也太复杂了,就是java正则的使用,感觉一般情况先应该用不上吧。
- 数字
script的逻辑表达式只有两种
- if else
- for 循环
语法
int x;
List y; add(ele) get(1) y[1]=5 y.length()
int x, y = 5, z;
def d;
int i = 10;
float[] f; f[0] f.length 数组类型
Map[][] m; get(key) m['a']='b' m.a='a_value'
String r = "some text"
这个语法有点类似python的语法,比较简练
4. script的使用方式
- 直接在某些query中使用
script
关键字
- search中使用script来产生额外的field使用的
script_field
关键字,然后再嵌套使用script
关键字
- search的
function_score
中使用script来自定义score时候使用的script_score
关键字,然后再嵌套使用script
关键字
- search中使用script来产生sort字段的时候在sort字段中嵌套使用
_script
关键字,然后再嵌套使用 script
关键字
5. 样例
1. 在search中使用 script_score功能
在 function_score 查询, script-based 排序(sorting), 或者 aggregations 有能力获取到 _score 字段
需要使用script_score 查询。
比如下面 是在function_score 中改变_score,其他类型的将来遇到再看
PUT my_index/_doc/1?refresh
{
"text": "quick brown fox",
"popularity": 1
}
PUT my_index/_doc/2?refresh
{
"text": "quick fox",
"popularity": 5
}
PUT my_index/_doc/3?refresh
{
"text": "quick fox",
"popularity": 50
}
GET my_index/_search
{
"query": {
"function_score": {
"query": {
"match": {
"text": "quick brown fox"
}
},
"script_score": {
"script": {
"lang": "expression",
"source": "_score * doc['popularity']"
}
}
}
}
}
返回
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 4.0225573,
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "3",
"_score" : 4.0225573,
"_source" : {
"text" : "quick fox",
"popularity" : 50
}
},
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.2483562,
"_source" : {
"text" : "quick brown fox",
"popularity" : 1
}
},
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.4022557,
"_source" : {
"text" : "quick fox",
"popularity" : 5
}
}
]
}
}
script base sorting
GET /_search
{
"query" : {
"term" : { "user" : "kimchy" }
},
"sort" : {
"_script" : {
"type" : "number",
"script" : {
"lang": "painless",
"source": "doc['field_name'].value * params.factor",
"params" : {
"factor" : 1.1
}
},
"order" : "asc"
}
}
}
agg操作等后面再总结。
2. 获取field的value,通过doc values
使用 doc[‘field_name’]
但是因为string.text field 没有doc-values 所以如果想用这种方式获取的话需要打开 fielddata 设置,这是一个很昂贵的操作。所以尽量不要使用script获取text field的内容。
PUT my_index/_doc/1?refresh
{
"cost_price": 100
}
GET my_index/_search
{
"script_fields": {
"sales_price": {
"script": {
"lang": "expression",
"source": "doc['cost_price'] * markup",
"params": {
"markup": 0.2
}
}
}
}
}
3. 获取field value 通过_source字段
通过_source.field_name 可以获取到source中对应的字段,但是相对来说比doc-values的要慢很多。
PUT my_index
{
"mappings": {
"properties": {
"first_name": {
"type": "text"
},
"last_name": {
"type": "text"
}
}
}
}
PUT my_index/_doc/1?refresh
{
"first_name": "Barry",
"last_name": "White"
}
GET my_index/_search
{
"script_fields": {
"full_name": {
"script": {
"lang": "painless",
"source": "params._source.first_name + ' ' + params._source.last_name"
}
}
}
返回主体
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"fields" : {
"full_name" : [
"Barry White"
]
}
}
]
上面的script.source中还必须要带params。
4. 获取field value 通过store field
store field是指那些在mapping中定义的mapping param 为 store:true
的field
可以使用 _fields[‘field_name’].value or _fields[‘field_name’] 语法
PUT my_index
{
"mappings": {
"properties": {
"full_name": {
"type": "text",
"store": true
},
"title": {
"type": "text",
"store": true
}
}
}
}
PUT my_index/_doc/1?refresh
{
"full_name": "Alice Ball",
"title": "Professor"
}
GET my_index/_search
{
"script_fields": {
"name_with_title": {
"script": {
"lang": "painless",
"source": "params._fields['title'].value + ' ' + params._fields['full_name'].value"
}
}
}
}
source field也是 store field的一种,所以_source field的性能和store field的性能接近,使用store filed的唯一场景是 _source特别大,我们并不需要获取全部的_source的时候 使用store field会更好。