在elasticsearch中简单的使用script_fields

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"           }         }       }     }   } } 

注意:

  1. hobbies其实是一个数组类型
  2. 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"}}  

注意:

  1. 需要注意一下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 运行结果

在elasticsearch中简单的使用script_fields

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字段,直接返回 falsen" +                                                             "          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 运行结果

在elasticsearch中简单的使用script_fields

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只对需要返回文档执行脚本,因此也不会太影响性能,除非返回的数据特别多。

在elasticsearch中简单的使用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

发表评论

评论已关闭。

相关文章