在elasticsearch中簡單的使用script_fields

1、背景

在我們使用es時,有些時候需要動態(tài)返回一些字段,而這些字段是通過動態(tài)計算得出的,那么此時該如何操作呢? 比如:我們索引中有一個sex字段,保存的是1或0,而在頁面上需要展示,那么這個時候就可以使用script_fields來解決。可能有些人說,我通過后臺進行格式化一下不就行了嗎,但是假設我們需要在kibana等可視化工具上展示呢?

2、準備數(shù)據(jù)

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其實是一個數(shù)組類型
  2. address是一個Object類型,即是一個復雜類型

2.2 插入數(shù)據(jù)

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的數(shù)據(jù)是沒有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 \"男\(zhòng)";\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 統(tǒng)計湖北的用戶有幾個

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("統(tǒng)計湖北省下的用戶有幾個")
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("http:// 因為 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://upload-images.jianshu.io/upload_images/9380897-d7d11467e1a724b0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

4、doc[..]和params[_source][..]有何不同

通過上面的案例,我們發(fā)現(xiàn),我們有些時候是通過doc[..]來訪問屬性的,有些時候是通過params['_source'][..]來訪問,那么這2種訪問方式有何不同呢?

doc[..]:使用doc關鍵字,將導致該字段的術語被加載到內存(緩存),這將導致更快的執(zhí)行,但更多的內存消耗。此外,doc[…]表示法只允許簡單的值字段(您不能從中返回json對象),并且僅對非分析或基于單個術語的字段有意義。然而,如果可能的話,使用doc仍然是訪問文檔值的推薦方法。
params[_source][..]: 每次使用_source都必須加載和解析, 因此使用_source會相對而言要慢點。

雖然訪問_source比訪問doc values要慢,但是script_fields只對需要返回文檔執(zhí)行腳本,因此也不會太影響性能,除非返回的數(shù)據(jù)特別多。

在這里插入圖片描述

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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容