乐趣区

关于后端:在elasticsearch中简单的使用scriptfields

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 运行后果

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

退出移动版