目录

一、概述

二、Scripting脚本使用


一、概述

Elasticsearch提供的增删改查相关API虽然能解决大部分业务场景的问题,但是在一些相对复杂的业务场景,使用增删改查不太好实现的时候,此时就需要借助Elasticsearch脚本进行实现,Elasticsearch脚本可以帮助我们解决复杂业务问题,如:自定义评分、自定义文本相关度、自定义过滤、自定义聚合分析等。

首先了解一下Scripting 使用语法:

"script": {
    "lang":   "...",  // 指定编写脚本的语言,默认为painless。
    "source" | "id": "...", // 指定脚本的来源,inline脚本是指定source,存储的脚本是指定的id,并从集群状态中检索
    "params": { ... } // 传递给脚本使用的变量参数。使用参数而不是硬编码值来减少编译时间。
  }

使用Elasticsearch脚本有两点需要注意的:

  • 1、性能问题

官方文档性能优化中明确指出使用脚本会导致性能低;

  • 2、使用场景相对少

非复杂业务场景下,基础的增、删、改、查基本上就能搞定。

Elasticsearch官方对Scripting脚本的介绍如下:

Scripting | Elasticsearch Guide [8.2] | Elastic

接下来我们通过几个比较简单的案例来说明Elasticsearch7.8环境下的脚本使用。

二、Scripting脚本使用

  • 1、创建一个索引库employee
PUT employee
{
  "mappings": {
    "properties": {
      "name": {
        "type": "keyword"
      },
      "realname": {
        "type": "keyword"
      },
      "age": {
        "type": "integer"
      },
      "birthday": {
        "type": "keyword"
      },
      "salary": {
        "type": "float"
      },
      "address": {
        "type": "keyword"
      }
    }
  }
}
  • 2、批量插入测试数据
POST /employee/_bulk
{"index":{"_id":1}}
{"name":"zs","realname":"张三","age":10,"birthday":"2018-12-27","salary":1000.0,"address":"广州市天河区科韵路50号"}
{"index":{"_id":2}}
{"name":"ls","realname":"李四","age":20,"birthday":"2017-10-20","salary":2000.0,"address":"广州市天河区珠江新城"}
{"index":{"_id":3}}
{"name":"ww","realname":"王五","age":30,"birthday":"2016-03-15","salary":3000.0,"address":"广州市天河区广州塔"}
{"index":{"_id":4}}
{"name":"zl","realname":"赵六","age":40,"birthday":"2003-04-19","salary":4000.0,"address":"广州市海珠区"}
{"index":{"_id":5}}
{"name":"tq","realname":"田七","age":50,"birthday":"2001-08-11","salary":5000.0,"address":"广州市天河区网易大厦"}
  • 3、案例一:输出员工的描述信息
GET employee/_search
{
  "script_fields": {
    "desc": {   // 该字段不存在 - script fields可以处理未存储的字段
      "script": {
        "lang": "painless",
        "source": "'姓名:'+ params._source.realname + ' - 年龄:' + params._source.age  + ' - 生日:' + params._source.birthday + ' - 地址:' + params._source.address"
      }
    }
  }
}
"hits" : [
      {
        "_index" : "employee",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "fields" : {
          "desc" : [
            "姓名:张三 - 年龄:10 - 生日:2018-12-27 - 地址:广州市天河区科韵路50号"
          ]
        }
      },
      {
        "_index" : "employee",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "fields" : {
          "desc" : [
            "姓名:李四 - 年龄:20 - 生日:2017-10-20 - 地址:广州市天河区珠江新城"
          ]
        }
      },
      {
        "_index" : "employee",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "fields" : {
          "desc" : [
            "姓名:王五 - 年龄:30 - 生日:2016-03-15 - 地址:广州市天河区广州塔"
          ]
        }
      },
      {
        "_index" : "employee",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 1.0,
        "fields" : {
          "desc" : [
            "姓名:赵六 - 年龄:40 - 生日:2003-04-19 - 地址:广州市海珠区"
          ]
        }
      },
      {
        "_index" : "employee",
        "_type" : "_doc",
        "_id" : "5",
        "_score" : 1.0,
        "fields" : {
          "desc" : [
            "姓名:田七 - 年龄:50 - 生日:2001-08-11 - 地址:广州市天河区网易大厦"
          ]
        }
      }
    ]

这里其实使用到Scripting脚本的自定义字段功能,原有Mapping并未定义“desc”字段。

  • 4、案例二:更新id为1的文档的age字段值
POST employee/_update/1
{
  "script" : {
    "source": "ctx._source.age += params.num",  // 新age = 旧age值 + 2
    "params" : {
      "num" : 2
    }
  }
}
  • 5、案例三:统计年龄大于等于30岁的员工的总工资
GET /employee/_search
{
  "size": 0, 
  "aggs": {
    "oh_total_salary_amt": {
      "sum": {
        "script": {
          "source": "if(doc['age'].value < 30) { return 0 ; } else { return doc['salary'].value}",
           "lang": "painless"
        }
      }
    }
  }
}
  • 6、案例四:统计年龄大于等于30岁的员工的人数
GET /employee/_search
{
  "size": 0, 
  "aggs": {
    "oh_total_count": {
      "cardinality": {
        "script": {
          "source": "if(doc['age'].value < 30) { return null ; } else { return  doc['_id'].value}",
           "lang": "painless"
        }
      }
    }
  }
}
  • 7、案例五:统计居住在“广州市天河区”的员工的人数
GET /employee/_search
{
  "size": 0, 
  "aggs": {
    "oh_total_count": {
      "cardinality": {
        "script": {
          "source": "def address = doc['address'].value; if(address.indexOf(params['keyword']) == -1) { return null ; } else { return doc['_id'].value}",
           "lang": "painless",
           "params": {
             "keyword": "广州市天河区"
           }
        }
      }
    }
  }
}

需要注意的是,ES每次执行scripts都是需要重新进行编译的,编译好的script可以缓存起来并供以后使用。所以如果script脚本里面有动态参数的话,尽可能使用script的params去指定,这样保证我们的script的source是不改变的,这样就只需要编译一次。下次调用的时候,只需要修改params里的参数即可,能减少脚本编译的次数,提高效率。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐