在elasticsearch中简单的使用script_fields - huan1993
source link: https://www.cnblogs.com/huan1993/p/17078649.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在我们使用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.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/5910495ac0814db393125dae96934e38.png)
4、doc[..]和params[_source][..]有何不同
通过上面的案例,我们发现,我们有些时候是通过doc[..]
来访问属性的,有些时候是通过params['_source'][..]
来访问,那么这2种访问方式有何不同呢?
doc[..]
:使用doc关键字,将导致该字段的术语被加载到内存(缓存),这将导致更快的执行,但更多的内存消耗。此外,doc[…]表示法只允许简单的值字段(您不能从中返回json对象),并且仅对非分析或基于单个术语的字段有意义。然而,如果可能的话,使用doc仍然是访问文档值的推荐方法。params[_source][..]
: 每次使用_source都必须加载和解析, 因此使用_source会相对而言要慢点。
虽然访问_source
比访问doc values
要慢,但是script_fields
只对需要返回文档执行脚本,因此也不会太影响性能,除非返回的数据特别多。
5、完整代码
6、参考文档
1、https://www.elastic.co/guide/en/elasticsearch/reference/8.6/search-fields.html#script-fields
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK