使用 JavaScript 脚本来进行复杂的查询改写
有这么一个需求:
网关里怎样对跨集群搜索进行支持的呢?我想实现: 输入的搜索请求是
lp:9200/index1/_search
这个索引在 3 个集群上,需要跨集群检索,也就是网关能否改成lp:9200/cluster01:index1,cluster02,index1,cluster03:index1/_search呢?
索引有一百多个,名称不一定是 app, 还可能多个索引一起的。
极限网关自带的过滤器 content_regex_replace 虽然可以实现字符正则替换,但是这个需求是带参数的变量替换,稍微复杂一点,没有办法直接用这个正则替换实现,有什么其他办法实现么?
使用脚本过滤器
当然有的,上面的这个需求,理论上我们只需要将其中的索引 index1 匹配之后,替换为 cluster01:index1,cluster02,index1,cluster03:index1 就行了。
答案就是使用自定义脚本来做,再复杂的业务逻辑都不是问题,都能通过自定义脚本来实现,一行脚本不行,那就两行。
使用极限网关提供的 JavaScript 过滤器可以很灵活的实现这个功能,具体继续看。
定义脚本
首先创建一个脚本文件,放在网关数据目录的 scripts 子目录下面,如下:
➜ gateway ✗ tree data
data
└── gateway
└── nodes
└── c9bpg0ai4h931o4ngs3g
├── kvdb
├── queue
├── scripts
│ └── index_path_rewrite.js
└── stats
这个脚本的内容如下:
function process(context) {
var originalPath = context.Get("_ctx.request.path");
var matches = originalPath.match(/\/?(.*?)\/_search/)
var indexNames = [];
if(matches && matches.length > 1) {
indexNames = matches[1].split(",")
}
var resultNames = []
var clusterNames = ["cluster01", "cluster02"]
if(indexNames.length > 0) {
for(var i=0; i<indexNames.length; i++){
if(indexNames[i].length > 0) {
for(var j=0; j<clusterNames.length; j++){
resultNames.push(clusterNames[j]+":"+indexNames[i])
}
}
}
}
if (resultNames.length>0){
var newPath="/"+resultNames.join(",")+"/_search";
context.Put("_ctx.request.path",newPath);
}
}
和普通的 JavaScript 一样,定义一个特定的函数 process 来处理请求里面的上下文信息,_ctx.request.path 是网关内置上下文的一个变量,用来获取请求的路径,通过 context.Get("_ctx.request.path") 在脚本里面进行访问。
中间我们使用了 JavaScript 的正则匹配和字符处理,做了一些字符拼接,得到新的路径 newPath 变量,最后使用 context.Put("_ctx.request.path",newPath) 更新网关请求的路径信息,从而实现查询条件里面的参数替换。
有关网关内置上下文的变量列表,请访问 Request Context
定义网关
接下来,创建一个网关配置,并使用 javascript 过滤器调用该脚本,如下:
entry:
- name: my_es_entry
enabled: true
router: my_router
max_concurrency: 10000
network:
binding: 0.0.0.0:8000
flow:
- name: default_flow
filter:
- dump:
context:
- _ctx.request.path
- javascript:
file: index_path_rewrite.js
- dump:
context:
- _ctx.request.path
- elasticsearch:
elasticsearch: dev
router:
- name: my_router
default_flow: default_flow
elasticsearch:
- name: dev
enabled: true
schema: http
hosts:
- 192.168.3.188:9206
上面的例子中,使用了一个 javascript 过滤器,并且指定了加载的脚本文件为 index_path_rewrite.js,并使用了两个 dump 过滤器来输出脚本运行前后的路径信息,最后再使用一个 elasticsearch 过滤器来转发请求给 Elasticsearch 进行查询。
启动网关
我们启动网关测试一下,如下:
➜ gateway ✗ ./bin/gateway
___ _ _____ __ __ __ _
/ _ \ /_\ /__ \/__\/ / /\ \ \/_\ /\_/\
/ /_\///_\\ / /\/_\ \ \/ \/ //_\\\_ _/
/ /_\\/ _ \/ / //__ \ /\ / _ \/ \
\____/\_/ \_/\/ \__/ \/ \/\_/ \_/\_/
[GATEWAY] A light-weight, powerful and high-performance elasticsearch gateway.
[GATEWAY] 1.0.0_SNAPSHOT, 2022-04-18 07:11:09, 2023-12-31 10:10:10, 8062c4bc6e57a3fefcce71c0628d2d4141e46953
[04-19 11:41:29] [INF] [app.go:174] initializing gateway.
[04-19 11:41:29] [INF] [app.go:175] using config: /Users/medcl/go/src/infini.sh/gateway/gateway.yml.
[04-19 11:41:29] [INF] [instance.go:72] workspace: /Users/medcl/go/src/infini.sh/gateway/data/gateway/nodes/c9bpg0ai4h931o4ngs3g
[04-19 11:41:29] [INF] [app.go:283] gateway is up and running now.
[04-19 11:41:30] [INF] [api.go:262] api listen at: http://0.0.0.0:2900
[04-19 11:41:30] [INF] [entry.go:312] entry [my_es_entry] listen at: http://0.0.0.0:8000
[04-19 11:41:30] [INF] [module.go:116] all modules are started
[04-19 11:41:30] [INF] [actions.go:349] elasticsearch [dev] is available
执行测试
运行下面的查询来验证查询结果,如下:
curl localhost:8000/abc,efg/_search
可以看到网关通过 dump 过滤器输出的调试信息:
---- DUMPING CONTEXT ----
_ctx.request.path : /abc,efg/_search
---- DUMPING CONTEXT ----
_ctx.request.path : /cluster01:abc,cluster02:abc,cluster01:efg,cluster02:efg/_search
查询条件按照我们的需求进行了改写,Nice!
重写 DSL 查询语句
好吧,我们刚刚只是修改了查询的索引而已,那么查询请求的 DSL 呢?行不行?
那自然是可以的嘛,瞧下面的例子:
function process(context) {
var originalDSL = context.Get("_ctx.request.body");
if (originalDSL.length >0){
var jsonObj=JSON.parse(originalDSL);
jsonObj.size=123;
jsonObj.aggs= {
"test1": {
"terms": {
"field": "abc",
"size": 10
}
}
}
context.Put("_ctx.request.body",JSON.stringify(jsonObj));
}
}
先是获取查询请求,然后转换成 JSON 对象,之后任意修改查询对象就行了,保存回去,搞掂。
测试一下:
curl -XPOST localhost:8000/abc,efg/_search -d'{"query":{}}'
输出:
---- DUMPING CONTEXT ----
_ctx.request.path : /abc,efg/_search
_ctx.request.body : {"query":{}}
[04-19 18:14:24] [INF] [reverseproxy.go:255] elasticsearch [dev] hosts: [] => [192.168.3.188:9206]
---- DUMPING CONTEXT ----
_ctx.request.path : /abc,efg/_search
_ctx.request.body : {"query":{},"size":123,"aggs":{"test1":{"terms":{"field":"abc","size":10}}}}
是不是感觉解锁了新的世界?
结论
通过使用 Javascript 脚本过滤器,我们可以非常灵活的进行复杂逻辑的操作来满足我们的业务需求。
使用 JavaScript 脚本来进行复杂的查询改写的更多相关文章
- MongoDB学习笔记-06 数据库命令、固定集合、GridFS、javascript脚本
介绍MongoDB支持的一些高级功能: 数据库命令 固定大小的集合 GridFS存储大文件 MongoDB对服务端JavaScript的支持 数据库命令 命令的原理 MongoDB中的命令其实是作为一 ...
- JavaScript脚本语言基础(四)
导读: JavaScript和DOM DOM文档对象常用方法和属性 DOW文档对象运用 JSON数据交换格式 正则表达式 1.JavaScript和DOM [返回] 文档对象模型(Document O ...
- [Javascript] 40个轻量级JavaScript脚本库
诸如jQuery, MooTools, Prototype, Dojo和YUI等JavaScript脚本库,大家都已经很熟悉.但这些脚本库有利也有弊--比如说JavaScript文件过大的问题.有时你 ...
- 获取当前正在执行的Javascript脚本文件的路径
获取当前JavaScript脚本文件的路径,在特定场景下可能需要,比如写模块加载器,或者进行日志记录.下面这段脚本适用于所有浏览器来获取正在执行js文件的路径,但是该方法只适用于脚本加载过程中执行的情 ...
- smarty模板中如何嵌入javascript脚本
[官方网站](http://www.php100.com/manual/smarty/) 在smarty文件里直接写javascript代码时候,造成500错误. javascript代码有很多的{} ...
- 在<a></a>标签中调用javascript脚本
有时候,我们点击了<a></a>标签(除了跳转到指定链接外)想要它调用某个方法,及调用javascript脚本,该如何做: 方法1:<a href="javas ...
- IE、chrome、火狐中如何调试javascript脚本
1. IE中点击"F12",在弹出页面中调试javascript脚本 2. chrome中点击"F12",在弹出页面中调试javascript脚本 3. 火 ...
- JavaScript脚本语言基础(一)
导读: JavaScript代码嵌入HTML文档 JavaScript代码运行方式 第一个实例 JavaScript的三种对话框 定义JavaScript变量 JavaScript运算符和操作符 Ja ...
- JavaScript脚本语言基础(二)
导读: JavaScript条件语句 JavaScript循环语句 JavaScript网页中错误捕获 JavaScript的Break和Continue命令 JavaScript的转义字符 1.Ja ...
- JavaScript脚本语言基础(三)
导读: 数学对象(Math) 数组对象(Array) 字符串对象(String) 日期对象(Date) js对象是属性和方法的集合.JavaScript中的所有事物都是对象,如:字符串.数值.数组.函 ...
随机推荐
- oracle表名、字段名等对象的命名长度限制(报错:ORA-00972: 标识符过长)
oracle表名.字段名等对象的命名长度限制(报错:ORA-00972: 标识符过长) 简单来说,出现了ORA-00972: 标识符过长的错误 找来找去发现是自己的中间表名太长导致的 Oracle数据 ...
- 力扣383(java&python)-赎金信(简单)
题目: 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成. 如果可以,返回 true :否则返回 false . m ...
- [FAQ] Docker查询出所有的停止容器并移除
$ docker rm `docker container ls -a --filter "status=exited" | awk '{print $1}' | sed '1,1 ...
- ARM 反汇编速成
1.跳转指令 B 无条件跳转 BL 带链接的无条件跳转 BX 带状态切换的无条件跳转 BLX 带链接和状态切换的无条件跳转 B loc_地址 BNE, BEQ 2.存储器与寄存器交互数据指令 ...
- [FAQ] golang-migrate/migrate error: migration failed in line 0: (details: Error 1065: Query was empty)
当我们使用 migrate create 创建了迁移文件. 没有及时填写内容,此时运行 migrate 的后续命令比如 up.down 会抛出错误: error: migration failed i ...
- k8s-1.28版本多master部署
一.环境准备 k8s集群角色 IP 主机名 安装相关组件 kubernetes版本号 控制节点 192.168.10.20 master apiserver.controller-manager.sc ...
- await this.$nextTick()和this.$nextTick(callback)有什么区别?记一次bug调试
背景 需要实现一个需求,一个小区业务详情页面,在左侧菜单栏切换了小区后,详情页跟着切换. 这个详情页面是根据url上的/:id来确定小区id的,所以切换了小区后,应该切换路由. 于是这样实现: wat ...
- Mybatis学习一(介绍/举例/优化)
MyBatis介绍: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis ...
- 一种光电容积波PPG 转换到心电图ECG进行房颤检测的神经网络模型
具体的软硬件实现点击 http://mcu-ai.com/ MCU-AI技术网页_MCU-AI人工智能 光电体积描记法(PPG)是一种经济有效的非侵入性技术,利用光学方法测量心脏生理学. PPG 在健 ...
- postgresql用sql查询表结构
查询sql如下: SELECT a.attname AS field, t.typname AS type, CASE WHEN t.typlen = -1 THEN a.atttypmod - 4 ...