1、背景
在咱们应用 es
时,有些时候须要动静返回一些字段,而这些字段是通过 动静计算
得出的,那么此时该如何操作呢?比方:咱们索引中有一个 sex
字段,保留的是 1 或 0
,而在页面上须要展现 男
或 女
,那么这个时候就能够应用 script_fields
来解决。可能有些人说
,我通过后盾进行格式化一下不就行了吗, 然而假如
咱们须要在 kibana
等可视化工具上展现呢?
2、筹备数据
2.1 mapping
PUT /index_script_fields
{
"mappings": {
"properties": {
"name":{"type": "keyword"},
"sex":{"type": "integer"},
"hobbies":{"type":"keyword"},
"address":{
"properties": {
"province":{"type":"keyword"},
"city":{"type":"keyword"}
}
}
}
}
}
留神:
hobbies
其实是一个数组类型address
是一个Object
类型,即是一个简单类型
2.2 插入数据
PUT /index_script_fields/_bulk
{"index":{"_id":1}}
{"name":"张三","sex":1,"hobbies":["足球","篮球"],"address":{"province":"湖北","city":"city01"}}
{"index":{"_id":2}}
{"name":"张三","sex":2,"address":{"province":"北京","city":"city01"}}
{"index":{"_id":3}}
{"name":"张三","hobbies":["足球"],"address":{"province":"湖北","city":"city01"}}
留神:
- 须要留神一下
id=3
的数据是没有sex
属性的,那么在painless
脚本中如何保障不报错。
3、案例
3.1 格式化性别 1- 男 2- 女 -1- 未知 如果不存在 sex 字段,则显示 – 其余的显示 **
3.1.1 dsl
GET /index_script_fields/_search
{
"query": {"match_all": {}
},
"_source": ["*"],
"script_fields": {
"sex_format": {
"script": {
"lang": "painless",
"source": """
// 判断 sex 字段是否存在
if(doc['sex'].size() == 0){return "--";}
if(doc['sex'].value == 1){return "男";}else if(doc['sex'].value == 2){return "女";}else if(doc['sex'].value == -1){return "未知";}else{return "**";}
"""
}
}
}
}
须要留神 sex
字段不存在,该如何判断,见上方的代码
3.1.2 java 代码
@Test
@DisplayName("格式化性别 1- 男 2- 女 -1- 未知 如果不存在 sex 字段,则显示 -- 其余的显示 **")
public void test01() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index(INDEX_NAME)
.query(query -> query.matchAll(matchAll -> matchAll))
// 不加这句,则 _source 不会返回,值返回 fields
.source(config -> config.filter(filter -> filter.includes("*")))
.scriptFields("sex_format", field ->
field.script(script ->
script.inline(inline ->
inline.lang(ScriptLanguage.Painless)
.source("// 判断 sex 字段是否存在 \n" +
"if(doc['sex'].size() == 0){\n" +
"return \"--\";\n" +
"}\n" +
"\n" +
"if(doc['sex'].value == 1){\n" +
"return \" 男 \";\n" +
"}else if(doc['sex'].value == 2){\n" +
"return \" 女 \";\n" +
"}else if(doc['sex'].value == -1){\n" +
"return \" 未知 \";\n" +
"}else{\n" +
"return \"**\";\n" +
"}")
)
)
)
.size(100)
);
System.out.println("request:" + request);
SearchResponse<Object> response = client.search(request, Object.class);
System.out.println("response:" + response);
}
3.1.3 运行后果
3.2 判断用户是否有某个喜好
3.2.1 dsl
GET /index_script_fields/_search
{"_source": ["*"],
"query": {"match_all": {}},
"script_fields": {
"has_hobby": {
"script": {
"lang": "painless",
"source": """
// 没有 hobbies 字段,间接返回 false
if(doc['hobbies'].size() == 0){return false;}
return doc['hobbies'].indexOf(params.hobby) > -1;
""","params": {"hobby":" 篮球 "}
}
}
}
}
3.2.2 java 代码
@Test
@DisplayName("判断用户是否有某个喜好")
public void test02() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index(INDEX_NAME)
.query(query -> query.matchAll(matchAll -> matchAll))
// 不加这句,则 _source 不会返回,值返回 fields
.source(config -> config.filter(filter -> filter.includes("*")))
.scriptFields("has_hobby", field ->
field.script(script ->
script.inline(inline ->
inline.lang(ScriptLanguage.Painless)
.source("// 没有 hobbies 字段,间接返回 false\n" +
"if(doc['hobbies'].size() == 0){\n" +
"return false;\n" +
"}\n" +
"return doc['hobbies'].indexOf(params.hobby) > -1;")
.params("hobby", JsonData.of("篮球"))
)
)
)
.size(100)
);
System.out.println("request:" + request);
SearchResponse<Object> response = client.search(request, Object.class);
System.out.println("response:" + response);
}
3.2.3 运行后果
3.3 统计湖北的用户有几个
3.3.1 dsl
GET /index_script_fields/_search
{"query": {"match_all": {}},
"aggs": {
"agg_province": {
"sum": {
"script": {
"lang": "painless",
"source": """
// 因为 address 是一个简单类型,因而不可间接通过 doc 来拜访
if(params['_source']['address']['province'] == '湖北'){return 1;}
return 0;
"""
}
}
}
}
}
因为 address 是一个简单类型,因而不可间接通过 doc 来拜访, 只能通过 params[_source]来拜访
3.3.2 java 代码
@Test
@DisplayName("统计湖北省下的用户有几个")
public void test03() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index(INDEX_NAME)
.query(query -> query.matchAll(matchAll -> matchAll))
// 不加这句,则 _source 不会返回,值返回 fields
.source(config -> config.filter(filter -> filter.includes("*")))
.aggregations("agg_province", agg->
agg.sum(sum ->
sum.script(script ->
script.inline(inline ->
inline.lang(ScriptLanguage.Painless)
// 因为 address 是一个简单类型,因而不可间接通过 doc 来拜访, 只可通过 params['_source']来拜访
.source("// 因为 address 是一个简单类型,因而不可间接通过 doc 来拜访 \n" +
"if(params['_source']['address']['province'] ==' 湖北 '){\n" +
"return 1;\n" +
"}\n" +
"return 0;")
)
)
)
)
.size(100)
);
System.out.println("request:" + request);
SearchResponse<Object> response = client.search(request, Object.class);
System.out.println("response:" + response);
}
3.3.3 运行后果
![运行后果![](https://img-blog.csdnimg.cn/5…)
4、doc[..]和 params_source 有何不同
通过下面的案例,咱们发现,咱们有些时候是通过 doc[..]
来拜访属性的,有些时候是通过 params['_source'][..]
来拜访,那么这 2 种拜访形式有何不同呢?
doc[..]
:应用 doc 关键字,将导致该字段的术语被加载到内存(缓存),这将导致更快的执行,但更多的内存耗费。此外,doc[…]表示法只容许简略的值字段(您不能从中返回 json 对象),并且仅对非剖析或基于单个术语的字段有意义。然而,如果可能的话,应用 doc 依然是拜访文档值的举荐办法。params[_source][..]
: 每次应用_source 都必须加载和解析, 因而应用_source 会相对而言要慢点。
尽管拜访 _source
比拜访 doc values
要慢,然而 script_fields
只对须要返回文档执行脚本,因而也不会太影响性能,除非返回的数据特地多。
5、残缺代码
https://gitee.com/huan1993/spring-cloud-parent/blob/master/es/es8-api/src/main/java/com/huan/es8/script/ScriptFieldApi.java
6、参考文档
1、https://www.elastic.co/guide/en/elasticsearch/reference/8.6/search-fields.html#script-fields