3

在elasticsearch中简单的使用script_fields - huan1993

 1 year ago
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.
neoserver,ios ssh client

在我们使用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.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、完整代码

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK