es 3期 第27节-运用Script脚本实现复杂需求

发布于:2025-03-28 ⋅ 阅读:(27) ⋅ 点赞:(0)

#### 1.Elasticsearch是数据库,不是普通的Java应用程序,传统数据库需要的硬件资源同样需要,提升性能最有效的就是升级硬件。
#### 2.Elasticsearch是文档型数据库,不是关系型数据库,不具备严格的ACID事务特性,任何企图直接替代严格事务性场景的应用项目都会失败!!!
#### 3.Elasticsearch原则上适合一切非事务性应用场景或能够容许一定的延迟的事务性场景;能最大限度的替代mongodb与传统关系型数据库

##### 索引字段与属性都属于静态设置,若后期变更历史数据需要重建索引才可生效
##### 对历史数据无效!!!!
##### 一定要重建索引!!!!

#### 1、Script概要介绍
### Script概念
## 概念解释
# ES的脚本类同传统数据库的UDF,可以自定义一些规则表达式,目的是满足日益复杂的应用需求
# Greenplum,支持Python/R
## 应用便利
# 支持灵活多变的应用场景与需求如:字段数值乘以10倍数

### 参数
# script,定义脚本查询入口
# lang,定义脚本语言
# source,定义脚本内容
# id,脚本编号名称,预定义脚本
# params,定义脚本参数,全局参数

# eg 乘以10
GET kibana_sample_data_flights/_search
{
  "query":{
    "match_all": {}
  },
  "script_fields":{
    "AvgTicketPrice_100":{
      "script":{
        "lang": "expression",
        "source": "doc['AvgTicketPrice']*10"
      }
    }
  }
}

### Script脚本类型
## 脚本类型
# 1.Painless,最丰富的强大的脚本语言
# 2.Lucene,简洁高效的原生lucene脚本语言
# 3.Mustache,简洁高效的template脚本语言
# 4.Java,原生支持iava高级自定义的脚本语言


### Script获取上下文数据值
## 获取上下文数据值
# 1.update script,数据更新上下文获取数据
# 2._score,数据分值
# 3.doc_value,从列式获取数据值
# 4._source,从原数据中获取数据值
# 5._store,从lucene存储中获取数据值

# update script,eg 从Script获取数据,更新字段值
GET kibana_sample_data_flights_3share/_search
POST kibana_sample_data_flights_3share/_update_by_query
{
  "script":{
    "source": """
      ctx._source.FlightNum=ctx._source.OriginCountry + "_"+ctx._source.FlightNum
    """
  }
}
GET kibana_sample_data_flights_3share/_search

# _score,eg 从Script获取数据,修改分值
GET kibana_sample_data_flights_3share/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "Origin": "Newark"
        }
      },
      "script_score": {
        "script": {
          "lang": "expression",
          "source": "_score*doc['FlightTimeMin']"
        }
      }
    }
  }
}

# doc_value,eg 从Script获取数据,修改分值
GET kibana_sample_data_flights_3share/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "Origin": "Newark"
        }
      },
      "script_score": {
        "script": {
          "lang": "expression",
          "source": "doc['FlightTimeMin']*10"
        }
      }
    }
  }
}
# _source,eg 从Script获取数据,更新字段值
GET kibana_sample_data_flights_3share/_search
POST kibana_sample_data_flights_3share/_search
{
  "script_fields": {
    "FlightNum": {
      "script": {
        "lang": "painless",
        "source": "params._source.FlightNum+'_'+ params._source.OriginCityName"
      }
    }
  }
}

# _store, eg 从lucene-store中获取数据
# 创建索引时需要设定store=true,否则出错
PUT dev-001
{
  "mappings":{
    "properties":{
      "devName":{
        "type":"text",
        "store":true
      },
      "devDesc":{
        "type":"text",
        "store":true
      }
    }
  }
}
POST dev-001/_doc
{
  "devName":"设备名称-1",
  "devDesc":"设备描述内容"
}
GET dev-001/_search
{
  "stored_fields":["*"],
  "script_fields":{
    "devInfo":{
      "script":{
        "lang": "painless",
        "source": "params._fields['devName'].value+'_'+params._fields['devDesc'].value"
      }
    }
  }
}

### 脚本接口(扩展了解)
## script_context
# 查询脚本执行支持的上下文参考

GET _script_context

## _script_language
# 查询内置已经注册脚本语言类型
# 内置脚本语言支持的上下文

GET _script_language


### scripts 创建
# 创建内置脚本,便于编译,提升执行能力
# *PUT _scripts/<script-id>
# *POST _scripts/<script-id>
# *PUT _scripts/<script-id>/<context>
# *POST _scripts/<script-id>/<context>

#### 2、Lucene脚本应用
## Lucene概要介绍
# 1.继承Lucene原生,简洁高效,表达能力有限
# 2.官方名称:expression
## 其它能力
# 1.性能最好

## expression, eg
# Lucene,继承原生lucene内置能力,性能好,简洁直接,性能同比其它,最好
# 查询参数
# lang,定义查询语言类型
# expression,lucene 语言类型
# doc,获取参数

# 获取日期的一天中的小时
GET kibana_sample_data_flights/_search
{
  "_source": [
    "timestamp"
  ],
  "track_total_hits": true,
  "script_fields": {
    "hourOfDay": {
      "script": {
        "lang": "expression",
        "source": "doc['timestamp'].date.hourOfDay"
      }
    }
  }
}
## 限制性条件
# 仅能获取数值、日期、geo_point类型

#### 3、Mustache脚本应用
# 基于 Mustache 模板语法规则,简单适用
# 查询模板

GET _search/template
{
  "source": {
    "query": {
      "match": {
        "{{my_field}}": "{{my_value}}"
      }
    },
    "size": "{{my_size}}"
  },
  "params": {
    "my_field": "message",
    "my_value":"foo",
    "my_size":10
  }
}

#### 4、Painless脚本应用深入探查
## 概要介绍
# 1.基于自定义脚本规则,语言语法参考python/java,简洁高效,语言能力丰富。
# 2.Painless是编译模型,基于ANTLR4与ASM库编译
# 3.内置沙箱机制,非常安全
# https://github.com/antlr/antlr4
# https://zhuanlan.zhihu.com/p/575048391

## 查询参数
# lang,定义查询语言类型
# painless,查询语言
# source,查询语言表达式

# eg
GET kibana_sample_data_flights/_search
{
  "query":{
    "match_all": {}
  },
  "script_fields":{
    "AvgTicketPrice_100":{
      "script":{
        "lang": "expression",
        "source": "doc['AvgTicketPrice']*10"
      }
    }
  }
}

### scripts/painless/_execute 测试接口
## 查询参数
# POST /scripts/painless/execute
# script,脚本表达式定义
# context,上下文逻辑表达式
# context_setup,上文参数,指定索引与字段替代等

## test-context
# 默认测试上下文
# 测试自定义变量

# eg 1
POST /_scripts/painless/_execute
{
  "script": {
    "source":"params.count/params.total",
    "params": {
      "count":11.0,
      "total":1100.0
    }
  }
}
  
# eg 2
POST /_scripts/painless/_execute
{
  "script": {
    "source":"""
    int i=10;
    float f=(float)i;
    float i_f=i*f;
    return i_f*params.p1;
    """,
    "params": {
      "p1": 100,
      "p2": 1000
    }
  }
}

## filter-context
# 过滤上下文,查询逻辑表达式
# bool 表达式

# eg 
POST /_scripts/painless/_execute
{
  "script": {
    "source":"""
    int i=10;
    float f=(float)i;
    float i_f=i*f;
    float f1= i_f*params.p1;
    return f1<doc['AvgTicketPrice'].value;
    """,
    "params": {
      "p1": 100,
      "p2": 1000
    }
  },
  "context":"filter",
  "context_setup":{
    "index":"kibana_sample_data_flights",
    "document":{
      "AvgTicketPrice":100.11
    }
  }
}

## score-context
# 数值上下文  

# eg 
POST /_scripts/painless/_execute
{
  "script": {
    "source":"""
    int i=10;
    float f=(float)i;
    float i_f=i*f;
    float f1= i_f*params.p1;
    return f1+doc['AvgTicketPrice'].value;
    """,
    "params": {
      "p1": 100,
      "p2": 1000
    }
  },
  "context":"score",
  "context_setup":{
    "index":"kibana_sample_data_flights",
    "document":{
      "AvgTicketPrice":100.11
    }
  }
}

### 限制性条件
## 性能限制
# 脚本语言越复杂,性能原则上越差
## 权限限制
# 脚本执行是在沙箱中进行,不能超过java 或者 es 权限限制边界,如禁止访问 IO 等
  
  
  
#### 5、Java脚本应用自定义能力探查
## 当以上3种脚本无法满足特定诉求,可以基于java 语言自定义构造特殊的脚本,如需要突破IO 访问限制
## 或者与其它框架集成等

## eg 
# 1、编写java插件 ExpertScriptPlugin.java
# 参考官方案例
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/modules-scripting-engine.html

# 2、编译运行包
# 基于 gradle 构件程序包
# jar包位置:elasticsearch-8.6.2\plugins\examples\script-expert-scoring\build\distrlbutions

# 3、复制到 Plugins 目录
# 例如我的目录是D:\Software\elasticsearch-8.6.2
# 进该目录下的plugins文件夹,这个文件夹默认是空的,在plugins下创建一个插件目录
# 将自己的jar复制进来

# 4、新增配置文件
#  新增配置文件plugin-descriptor.properties
#(可以在elasticsearch-8.6.2\modules\x-pack-async目录下拷一份过来改改)


# 重启es,通过_script_language查看支持的脚本,比原先多一个
GET _script_language

## 官方插件原代码位置
# elasticsearch-8.6.2\plugins\examples\script-expert-scoring\src\main\java\org\elasticsearch\example\expertscript\ExpertScriptPlugin.java


  
#### 6、Script应用场景
### 应用场景
# 脚本应用场景
# 查询:query
# 自定义字段:script_field
# 排序:sort
# 聚合:aggs
# 上卷:transform
# 分值:scrore
  
  
  
  
#### 7、Script经验分享与总结
# 选择合适的脚本
# 注意脚本性能问题
  
  
# scripting 脚本模块
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/modules-scripting.html
# lucene-expressions 语法参考
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/modules-scripting-expression.html
# lucene-expressions 官方参考
# https://lucene.apache.org/core/8_8_2/expressions/index.html?org/apache/lucene/expressions/js/package-summary.html
# painless 语言参考
# https://www.elastic.co/guide/en/elasticsearch/painless/8.6/index.html
# painless-execute-api 测试接囗
# https://www.elastic.co/guide/en/elasticsearch/painless/8.6/painless-execute-api.html
# search-template 查询模板
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/search-template.html
# Java 脚本插件
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/modules-scripting-engine.html
# script-apis 执行接口
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/script-apis.html
# mapping-fields 索引元数据参考
# https://www.elastic.co/guide/en/elasticsearch/reference/8.6/mapping-fields.html