Tencent / APIJSON

🏆 实时 零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构 🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and the returned JSON of API can be customized by Frontend(Client) users
http://apijson.cn
Other
17.1k stars 2.14k forks source link

apijson json服务编排 #482

Open cloudAndMonkey opened 1 year ago

cloudAndMonkey commented 1 year ago

Description

@TommyLemon 功能描述: 一个服务编排的伪码流程设计 将伪码转换为 apijson json、前置、后置函数、javascript、redis、elasticSearch等 并且能够mork每一步,执行中间流程,还能查询每个阶段执行的sql语句,及结果.

json格式:

{
    "@transaction": true, 
    "id@-()": "getCurrentUserId()", // 前置函数
    "@post": [
        "xxx"
    ], 
    "xxx": {
     "@method": "xxx",
      "@version": "", // 版本,控制校验
      "@datasource": "" // 数据源相关配置
     // 分表规则等,后面再说
   }
"@explain": true
}

1、独立定义一个url method, 通过解析不同method执行不同流程 和已有method区分开,避免歧义 2、最外层新增传参 "transaction": true 来指定开启事务 3、控制每条语句的数据源 4、完善 “@Explain" 如果没有执行计划,则返回sql语句. 能够在 reponse返回值中, 看到json中执行的每条sql,方便排错 5、@version支持 定义不同场景的 新增、修改、删除等执行规则. 请通过version版本区分 6、前置函数 1) 要能拿到其他数据源 2) 能拿到json执行过程中的数据 3) 拿到当前数据源(和外部json一个事物执行) 4) 从数据库/或其他数据源 查询获取对照关系 组装数据, 调用对应数据源的api方法执行即可 5) 前置函数,能调用其他函数 把一些计算, 执行,计算 等 放到一个函数进行整合 还没细化 7、支持mork 通过伪码,分解为不同 阶段 的json语句执行

测试点: 1、测试 一个json多条语句,后置函数啥时候执行 2、操作其他数据源, 事物是json执行完才会提交, 需要保证一致性 3、redis、elasticSearch、javascript、lua等功能测试

cloudAndMonkey commented 1 year ago

@TommyLemon apiauto能支持场景测试吗? 还要能mork

TommyLemon commented 1 year ago

@TommyLemon apiauto能支持场景测试吗?

目前不支持多接口串联的场景测试。 后续先支持 前置和后置处理 脚本,满足比较普遍的需求。

cloudAndMonkey commented 1 year ago

@TommyLemon apiauto能支持场景测试吗?

目前不支持多接口串联的场景测试。 后续先支持 前置和后置处理 脚本,满足比较普遍的需求。

嗯嗯, 能实现前置、后置, 再搞一个for循环, 人家通过for循环id去构建数据, 进行简单接口压测😊 现在这种测试工具也很多,是否有这个必要,花这么大力气搞这个 https://metersphere.io/docs/v2.x/ 他们家已经做了一个通用的测试整合平台

TommyLemon commented 1 year ago

循环测试,目前可以直接输入随机与顺序测试的数量来实现。至于压测,建议用专门的工具,例如 JMeter。 APIAuto 专注于接口的文档与测试这两块,为前后端开发和接口测试人员降本增效。

MeterSphere 这种大而全却没有突出特点的接口工具/平台太多了,不是 APIAuto 的发展方向。 APIAuto 的 Request JSON 键值对后绿色注释、Response JSON key 悬浮注释、静态检查、自动补全、零代码回归测试远不是这种项目能比的,可以多了解下,尤其是我在 QECon-全球软件质量&效能大会的分享。 https://github.com/TommyLemon/APIAuto

cloudAndMonkey commented 1 year ago

循环测试,目前可以直接输入随机与顺序测试的数量来实现。至于压测,建议用专门的工具,例如 JMeter。 APIAuto 专注于接口的文档与测试这两块,为前后端开发和接口测试人员降本增效。

MeterSphere 这种大而全却没有突出特点的接口工具/平台太多了,不是 APIAuto 的发展方向。 APIAuto 的 Request JSON 键值对后绿色注释、Response JSON key 悬浮注释、静态检查、自动补全、零代码回归测试远不是这种项目能比的,可以多了解下,尤其是我在 QECon-全球软件质量&效能大会的分享。 https://github.com/TommyLemon/APIAuto

哈哈,好的, 我空了好好研究一下这块 metersphere 它做了性能测试工具jmeter等的一个整合

cloudAndMonkey commented 1 year ago

@TommyLemon 多数据源测试: mysql不同数据库测试 单条sql语句执行:

{ "User:aa":{ "username":"test-3", "password": "233223", "state": 1, "@schema": "apijsonMuli1", "@datasource": "slave", "@database": "MYSQL" }, "tag": "User", "@explain": true }

简化配置:通过数据源自动匹配@schema、@database等信息 json { "User:aa":{ "username":"test-3", "password": "233223", "state": 1, "@datasource": "slave" }, "tag": "User", "@explain": true }

全局事物: { "@datasource": "slave", "User:aa":{ "username":"test-3", "password": "233223", "state": 1 }, "tag": "User", "@explain": true } inner join 同一个mysql,不同数据库 { "format": true, "[]": { "join":"&/User_address/user_id@", "User": { "@column": "id,username", "@datasource": "slave" }, "User_address": { "user_id@": "/User/id", "@column": "id:id1,addr", "@datasource": "master" } }, "@explain": true } image

1、多数据源配置 我本地采用 https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter 1、业务系统实现接口支持多数据源 image

image

image

image

TommyLemon commented 1 year ago

赞,这个依赖了 SpringBoot 及 dynamic-datasource-spring-boot-starter,可以通过新增 Demo 的方式来贡献哦 https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server

image
cloudAndMonkey commented 1 year ago

赞,这个依赖了 SpringBoot 及 dynamic-datasource-spring-boot-starter,可以通过新增 Demo 的方式来贡献哦 https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server image 等我弄完差不多,支持crud ,一起提交 也可以不依赖 dynamic-datasource-spring-boot-starter, 实现都差不多.就是通过数据源名字, 去解析获取不同的数据源,然后拿到用户名,密码,url等信息

cloudAndMonkey commented 1 year ago

目前"@datasource"的解析顺序: 1、全局 "@datasource"

{
    `"@datasource"`: "slave",
    "User:aa":{
        "username":"test-3",
        "password": "233223",
        "state": 1
    },
    "tag": "User",
    "@explain": true
}

如果全局配置 "@datasource", 则会覆盖 对象中的 "@datasource" 2、不配置全局 "@datasource" 应用对象内的 "@datasource" // 简化调用

{
    "User:aa":{
        "username":"test-3",
        "password": "233223",
        "state": 1,
        `"@datasource"`: "slave"
    },
    "tag": "User",
    "@explain": true
}

3、如果没有配置"@datasource"(全局/对象内部) 使用应用默认数据源 image

cloudAndMonkey commented 1 year ago

@TommyLemon crud 多数据源使用:

{
    "@datasource:xxx数据源": "User", //数据源
    "@datasource:xxx1数据源": "User_address[]", //数据源
    "@datasource": "全局默认数据源", 
    "@post": "xxx,xxx[]",
    "User": {
        "id": 82001
    },
    "User_address[]": [
        {
        "user_id@": "User:aa/id",
        "addr": "ddd",
        "count": 1
        },
        {
        "user_id@": "User:aa/id",
        "addr": "ddd1",
        "count": 2
        }
    ],
   "@put": "User_address:modifUA",
   "User_address:modifUA": 
    {
        "id": "8029f53f-6776-49c7-9a0b-8bb3a2c0e99d",
        "user_id@": "User:aa/id",
        "addr": "addr-01",
        "count": 1
    },
    "@delete": "User:delUser",
    "User:delUser":{
        "id{}": ["a6043182-c270-4971-bc87-c87e742cf5e8", "c5bca4aa-28cf-4740-ae7b-42567295d183"]
    },
    "@explain": true
} 

解析执行顺序: 1、"@datasource:xxx数据源" 2、没有配置 "@datasource:xxx数据源", 使用 "@datasource": "全局默认数据源" 3、"@datasource:xxx数据源", "@datasource": "全局默认数据源" 都没有配置 使用系统默认数据源

事物: crud 1、相同数据源 开启事物,使用一个connection 极端情况 crud 全部是查询语句 开启了手动提交, 最后没有提交, 但是connection 会被关闭, 没有影响 使用crud, 就是想执行多条语句, 并支持事物 先这样吧, 以业务来驱动,逐步完善 最后: Parser.rollback 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 rollback; Parser.commit 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 commit; Parser.close 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 close;

后面有业务需求,或者 你有更好的解决方案, 再支持 "@transaction": true 1、"@transaction": true 相同数据源的对象使用一个connection 最后: Parser.rollback 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 rollback; Parser.commit 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 commit; Parser.close 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 close; 2、"@transaction": false 或者不配置 单个对象、数组 独立使用connection 3、get/head 对象 是否独立connection执行,还是跟着 "@datasource:xxx数据源"走?

TommyLemon commented 1 year ago

目前"@datasource"的解析顺序: 1、全局 "@datasource"

{
  `"@datasource"`: "slave",
    "User:aa":{
        "username":"test-3",
        "password": "233223",
      "state": 1
    },
    "tag": "User",
    "@explain": true
}

如果全局配置 "@datasource", 则会覆盖 对象中的 "@datasource" 2、不配置全局 "@datasource" 应用对象内的 "@datasource" // 简化调用

{
    "User:aa":{
        "username":"test-3",
        "password": "233223",
      "state": 1,
        `"@datasource"`: "slave"
    },
    "tag": "User",
    "@explain": true
}

3、如果没有配置"@datasource"(全局/对象内部) 使用应用默认数据源 image

优先局部关键词,全局的关键词只有在局部未设置时才对局部生效,和 @database, @schema, @role 等逻辑一样,类似 Java 局部变量和成员变量重名的处理方式 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L309-L326

image
TommyLemon commented 1 year ago

@TommyLemon crud 多数据源使用:

{
    "@datasource:xxx数据源": "User", //数据源
    "@datasource:xxx1数据源": "User_address[]", //数据源
    "@datasource": "全局默认数据源", 
    "@post": "xxx,xxx[]",
    "User": {
        "id": 82001
    },
    "User_address[]": [
      {
      "user_id@": "User:aa/id",
      "addr": "ddd",
      "count": 1
      },
      {
      "user_id@": "User:aa/id",
      "addr": "ddd1",
      "count": 2
      }
    ],
   "@put": "User_address:modifUA",
   "User_address:modifUA": 
  {
      "id": "8029f53f-6776-49c7-9a0b-8bb3a2c0e99d",
      "user_id@": "User:aa/id",
      "addr": "addr-01",
      "count": 1
  },
  "@delete": "User:delUser",
    "User:delUser":{
      "id{}": ["a6043182-c270-4971-bc87-c87e742cf5e8", "c5bca4aa-28cf-4740-ae7b-42567295d183"]
    },
    "@explain": true
} 

解析执行顺序: 1、"@datasource:xxx数据源" 2、没有配置 "@datasource:xxx数据源", 使用 "@datasource": "全局默认数据源" 3、"@datasource:xxx数据源", "@datasource": "全局默认数据源" 都没有配置 使用系统默认数据源

事物: crud 1、相同数据源 开启事物,使用一个connection 极端情况 crud 全部是查询语句 开启了手动提交, 最后没有提交, 但是connection 会被关闭, 没有影响 使用crud, 就是想执行多条语句, 并支持事物 先这样吧, 以业务来驱动,逐步完善 最后: Parser.rollback 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 rollback; Parser.commit 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 commit; Parser.close 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 close;

后面有业务需求,或者 你有更好的解决方案, 再支持 "@transaction": true 1、"@transaction": true 相同数据源的对象使用一个connection 最后: Parser.rollback 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 rollback; Parser.commit 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 commit; Parser.close 时统一遍历 datasourceSQLExecutorMap 的所有 SQLExecutor 来 close; 2、"@transaction": false 或者不配置 单个对象、数组 独立使用connection 3、get/head 对象 是否独立connection执行,还是跟着 "@datasource:xxx数据源"走?

1、"@datasource:xxx数据源" 这种写法容易有兼容问题,尤其是在搭配 APIJSON 引用赋值( "@datasource:a/b": "User", "k@": "@datasource:a/b" )其它功能或生态项目 APIAuto 参数注入配置( @datasource:a/b: 'slave' )等使用的时候,我们对 @datasource 的值除了类型为 String 外没有其它要求,可能出现各种字符。可以参考 join 的处理 https://github.com/Tencent/APIJSON/blob/master/Document.md#3.2

image

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1370-L1384

image

改为

{
  "@post": {  // @put 同样处理,至于 @get, @gets, @head, @heads, @delete 本来就不处理 Table[]:[],所以可以不支持。
     "User_address[]": {
        "@datasource": "xxx1数据源"
        // 还可以带 @role 等更多的关键词
     },
     // 其它对象
  }
}

至于不是 Table[]:[] 数组的方式,都可以直接按现有方式在对象内传对应的关键词键值对。

{
     "User": {
        "@datasource": "xxx数据源"
        // 还可以带 @role 等更多的关键词
     }
}
cloudAndMonkey commented 1 year ago

哎呀, 确实合理 ,我的,我的.学习了

TommyLemon commented 1 year ago

事务 CRUD: 1、全是查询语句的话,可以用 GET。 HEAD/HEADS 不过是在 GET/GETS 的基础上没传 @column 时指定 SELECT count(*) AS count 或者传了 @column:"key" 时指定 SELECT count(key) AS count ,以及不允许传 key[]:{},完全可以用 GET/GETS 声明 @column: "count(*):count", @column: "count(key):count" 来替代。 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1657-L1729

image

当然如果传数组关键词 compat: true,那么就是

SELECT count(*) AS count FROM(SELECT 子查询)

这种方式和 Mybatis 各种分页插件的统计处理一致,性能较差,所以 APIJSON 默认不开启; 但因为兼容性更好、某些场景需要,所以 APIJSON 提供开启参数。

@transaction 1 & 2、走 /crud 接口的话,直接就默认为全局事务处理,@transaction 先不实现问题也不大,确实可以看有没有比较多的用户有这个需求,或者我们自己有比较强的需求,又没有好的替代方式时再实现。

cloudAndMonkey commented 1 year ago

事务 CRUD: 1、全是查询语句的话,可以用 GET。 HEAD/HEADS 不过是在 GET/GETS 的基础上没传 @column 时指定 SELECT count() AS count 或者传了 @column:"key" 时指定 SELECT count(key) AS count ,以及不允许传 key[]:{},完全可以用 GET/GETS 声明 @column: "count():count", @column: "count(key):count" 来替代。 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1657-L1729 image

当然如果传数组关键词 compat: true,那么就是

SELECT count(*) AS count FROM(SELECT 子查询)

这种方式和 Mybatis 各种分页插件的统计处理一致,性能较差,所以 APIJSON 默认不开启; 但因为兼容性更好、某些场景需要,所以 APIJSON 提供开启参数。

@transaction 1 & 2、走 /crud 接口的话,直接就默认为全局事务处理,@transaction 先不实现问题也不大,确实可以看有没有比较多的用户有这个需求,或者我们自己有比较强的需求,又没有好的替代方式时再实现。

学习了, 我慢慢理一下, 谢谢

cloudAndMonkey commented 1 year ago

@TommyLemon 看了你的解决方案, 我昨天整理多数据源的思路, 是想延续 @get、@post方式,全局设置,不用对象内部定义@method 把 "@datasource:xxx数据源": "User", //数据源 改为:

"@datasource":{
"master数据源": "User,UserAddress[]", //   代表对象内定义的数据源
"slave数据源": "User,UserAddress[]", 
"_default": "全局默认数据源" // 全局默认数据源
}

解析顺序: 1、"master数据源": "User,UserAddress[]", // 代表对象内定义的数据源 2、如果对象、数组没有定义数据源, 使用 "_default": "全局默认数据源" // 全局默认数据源 3、如果 全局默认数据源 没有找到, 匹配应用默认数据源 image

另外,我没有考虑将 其他功能点 进行归纳,整合, 比如 数据源、schema、版本、角色、method等属性, 这些都是跟着 对象或数组的key走的

{
  "@post": {  // @put 同样处理,至于 @get, @gets, @head, @heads, @delete 本来就不处理 Table[]:[],所以可以不支持。
     "User_address[]": {
        "@datasource": "xxx1数据源"
        // 还可以带 @role 等更多的关键词
     },
     // 其它对象
  }
}

1、将这些属性解析生成: 对象/数组 key: { datasource: xxx, schema:xxx, role:xxxx method: xxx version:xxx 其他属性: xxxx } 2、将这些属性 加入 对象、数组解析生成的对象json中 3、完成功能开发、做好兼容.

感谢你的耐心解答,哈哈

TommyLemon commented 1 year ago

其实不需要具体解析,有啥就填充啥到对应 JSON 对象中(除非已有)。

entrySet = request.entrySet();
for(Entry<String, Object> entry : entrySet) {
   if (entry.getValue() != null && jsonObj.get(entry.getKey()) == null) {
      jsonObj.put(entry.getKey(), entry.getValue());
   }
}

原来的解析逻辑会处理各种关键词。

cloudAndMonkey commented 1 year ago

其实不需要具体解析,有啥就填充啥到对应 JSON 对象中(除非已有)。

entrySet = request.entrySet();
for(Entry<String, Object> entry : entrySet) {
   if (entry.getValue() != null && jsonObj.get(entry.getKey()) == null) {
      jsonObj.put(entry.getKey(), entry.getValue());
   }
}

原来的解析逻辑会处理各种关键词。

我的意思: 是apijson框架解析数组 json, 转换为 数组-对象 json, 再调用 vertify 进行校验, 这个时候需要把数组配置的字段填充进对象 校验,没有对内容进行解析, 把"@post" 配置信息填充进去就可以了 { "@post": { "User:aa": { "@datasource": "master" }, "User_address[]": { "@datasource": "slave" } }, "User:aa":{ "username":"test-3", "password": "233223", "state": 1 }, "User_address[]": [ { "user_id@": "User:aa/id", "addr": "ddd", "count": 1 }, { "user_id@": "User:aa/id", "addr": "ddd1", "count": 2 } ], "tag": "User", "@explain": true } 我再调一下connect commit、rollback 基本就能通了. 然后我把version、role都测了

TommyLemon commented 1 year ago

/crud 接口可能传参有 增删改,AbstractParser.ENABLE_VERIFY_CONTENT == true 时(如果实现了 trasaction: true 则 && 这个判断)默认和 增删改 一样必须走 verifyContent,用 Request 表的 structure 等校验规则来校验,避免出现安全漏洞。

cloudAndMonkey commented 1 year ago

已经通了,哈哈

{
    `"@datasource"`: "db2",
    "@post": {
        "User:aa": {
            // "@datasource": "db1"
        },
        "User_address[]": {
            "@datasource": "db1"
        }
    },
    "@put": {
        "User_address:modifUA": {
            "@datasource": "db1"
        }
    },
    "@delete": {
        "User:delUser": {
            "@datasource": "db2"
        }
    },
    "User:aa":{
        "username":"test-3",
        "password": "233223",
        "state": 1
    },

    "User_address[]": [
        {
        "user_id@": "User:aa/id",
        "addr": "ddd",
        "count": 1
        },
        {
        "user_id@": "User:aa/id",
        "addr": "ddd1",
        "count": 2
        }
    ],
    "User_address:modifUA": 
    {
        "id": "ea1bb2a7-b27b-4173-9617-b9541f65fbd6",
        "user_id@": "User:aa/id",
        "addr": "addr-01",
        "count": 1
    },
    "User:delUser":{
        "id@": "User:aa/id"
    },
    "@explain": true
}

解析顺序: 1、 对象内定义的数据源 2、 "@datasource"全局默认数据源 3、1、2没有找到, 匹配应用默认数据源

{
    // "@datasource": "db2",
    "@post": {
        "User:aa": {
            // "@datasource": "db1"
        },
        "User_address[]": {
            // "@datasource": "db1"
        }
    },
    "@put": {
        "User_address:modifUA": {
            // "@datasource": "db1"
        }
    },
    "@delete": {
        "User:delUser": {
            // "@datasource": "db2"
        }
    },
    "User:aa":{
        "username":"test-3",
        "password": "233223",
        "state": 1
    },

    "User_address[]": [
        {
        "user_id@": "User:aa/id",
        "addr": "ddd",
        "count": 1
        },
        {
        "user_id@": "User:aa/id",
        "addr": "ddd1",
        "count": 2
        }
    ],
    "User_address:modifUA": 
    {
        "id": "0c999988-2d53-4f26-9884-6a63201d36e3",
        "user_id@": "User:aa/id",
        "addr": "addr-01",
        "count": 1
    },
    "User:delUser":{
        "id@": "User:aa/id"
    },
    "@explain": true
}

@TommyLemon void rollback(Savepoint savepoint) throws SQLException; 是你为数组执行多条sql语句, 设计的吗? 我看现在没有用起来? image

cloudAndMonkey commented 1 year ago

/crud 接口可能传参有 增删改,AbstractParser.ENABLE_VERIFY_CONTENT == true 时(如果实现了 trasaction: true 则 && 这个判断)默认和 增删改 一样必须走 verifyContent,用 Request 表的 structure 等校验规则来校验,避免出现安全漏洞。

收到 子查询sql@ 数据源 {

"sql@": { "with": true, "from": "User_address", "User_address": { "@column": "user_id", "id{}": ["ba0e59db-91e1-4d02-adc5-077c46537a6f"] } }, "User:delUser": { "@datasource": "db1", "id{}@": "sql" }, "explan": true } 子查询的数据源, 目前是没有处理,使用系统默认 子查询 sql@ json不需要配置, 直接和主语句使用一个datasource吗?

TommyLemon commented 1 year ago

已经通了,哈哈

{
  `"@datasource"`: "db2",
  "@post": {
      "User:aa": {
          // "@datasource": "db1"
      },
      "User_address[]": {
          "@datasource": "db1"
      }
  },
  "@put": {
      "User_address:modifUA": {
          "@datasource": "db1"
      }
  },
  "@delete": {
      "User:delUser": {
          "@datasource": "db2"
      }
  },
    "User:aa":{
        "username":"test-3",
        "password": "233223",
      "state": 1
    },

    "User_address[]": [
      {
      "user_id@": "User:aa/id",
      "addr": "ddd",
      "count": 1
      },
      {
      "user_id@": "User:aa/id",
      "addr": "ddd1",
      "count": 2
      }
    ],
    "User_address:modifUA": 
  {
      "id": "ea1bb2a7-b27b-4173-9617-b9541f65fbd6",
      "user_id@": "User:aa/id",
      "addr": "addr-01",
      "count": 1
  },
    "User:delUser":{
      "id@": "User:aa/id"
    },
    "@explain": true
}

解析顺序: 1、 对象内定义的数据源 2、 "@datasource"全局默认数据源 3、1、2没有找到, 匹配应用默认数据源

{
  // "@datasource": "db2",
  "@post": {
      "User:aa": {
          // "@datasource": "db1"
      },
      "User_address[]": {
          // "@datasource": "db1"
      }
  },
  "@put": {
      "User_address:modifUA": {
          // "@datasource": "db1"
      }
  },
  "@delete": {
      "User:delUser": {
          // "@datasource": "db2"
      }
  },
    "User:aa":{
        "username":"test-3",
        "password": "233223",
      "state": 1
    },

    "User_address[]": [
      {
      "user_id@": "User:aa/id",
      "addr": "ddd",
      "count": 1
      },
      {
      "user_id@": "User:aa/id",
      "addr": "ddd1",
      "count": 2
      }
    ],
    "User_address:modifUA": 
  {
      "id": "0c999988-2d53-4f26-9884-6a63201d36e3",
      "user_id@": "User:aa/id",
      "addr": "addr-01",
      "count": 1
  },
    "User:delUser":{
      "id@": "User:aa/id"
    },
    "@explain": true
}

@TommyLemon void rollback(Savepoint savepoint) throws SQLException; 是你为数组执行多条sql语句, 设计的吗? 我看现在没有用起来? image

rollback 是为整个请求设计的,不限于数组,只要有多个对象就可能执行多条 SQL。 rollback() 失败就 rollback(Savepoint savepoint),是尽可能保证回滚成功的保底机制。 https://github.com/Tencent/APIJSON/blob/f71b08495e02808dabf1099f4ec8eee5a5ee692d/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java#L1193

image
TommyLemon commented 1 year ago

/crud 接口可能传参有 增删改,AbstractParser.ENABLE_VERIFY_CONTENT == true 时(如果实现了 trasaction: true 则 && 这个判断)默认和 增删改 一样必须走 verifyContent,用 Request 表的 structure 等校验规则来校验,避免出现安全漏洞。

收到 子查询sql@ 数据源 {

"sql@": { "with": true, "from": "User_address", "User_address": { "@column": "user_id", "id{}": ["ba0e59db-91e1-4d02-adc5-077c46537a6f"] } }, "User:delUser": { "@datasource": "db1", "id{}@": "sql" }, "explan": true } 子查询的数据源, 目前是没有处理,使用系统默认 子查询 sql@ json不需要配置, 直接和主语句使用一个datasource吗?

SQL 子查询, SQL 主表 JOIN 副表 最终都是生成和执行一条 SQL,必须在同一个 connection 中(@datasource, @database, method, @role 当然也必须相同)。

cloudAndMonkey commented 1 year ago

@TommyLemon 1、elasticSearch 索引名必须为小写字母 { "index" : { "_index" : "Blog", "_type" : "_doc", "_id" : "2", "status" : 400, "error" : { "type" : "invalid_index_name_exception", "reason" : "Invalid index name [Blog], must be lowercase", "index_uuid" : "na", "index" : "Blog" } } }

image

2、elasticSearch 没有数据库housekeeping , 并且index 不能包含特殊字符 { "ES_blog:a": { "@datasource": "ELASTICSEARCH" }, "@explain": true }

EXPLAIN SELECT * FROM housekeeping.User LIMIT 1 Caused by: ["sys"."ES_blog"] IndexNotFoundException[no such index ["sys"."ES_blog"]]

需要这里判断,如果是 isElasticsearch, 不需要添加"`" , "\""; image

TommyLemon commented 1 year ago

就按 Elasticsearch 的要求判断下,对应处理

cloudAndMonkey commented 1 year ago

@TommyLemon 单条查询已经成功了 image 分页查询: image

head 查询总数 elasticSearch 总数返回是double类型, 比如 6.0 image 分组查询 image

elasticSearch 索引 mapping 和document内容是分开存储的. jdbc 查询拿不到 数据类型. image

elasticSearch 索引 mapping 和document内容 shi fen是分开存储的. jdbc 查询是拿不到 数据类型的. 还有一些兼容细节,我要测一下 1) 不支持sql explain 2) where 字段需要支持keyword SELECT count,count(*) as total FROM es_blog where title.keyword = "Linux安装" group by count 3) elasticSearch-sql google浏览器插件可以将sql语句转换为 elasticsearh dsl语句 image 代码应该也可以 image

cloudAndMonkey commented 1 year ago

@TommyLemon 新增/修改/删除 elasticSearch/redis , 非 sql, 咋弄? 远程函数?

cloudAndMonkey commented 1 year ago

@TommyLemon 请问调用js脚本, 要咋弄呀, 我也测一下

TommyLemon commented 1 year ago

@TommyLemon 新增/修改/删除 elasticSearch/redis , 非 sql, 咋弄? 远程函数?

和 SQL 差距大的话,改造 AbstractSQLConfig 的工作量也会比较大,如果目前没有比较强的需求,可以先用远程函数等方式替代。 Elasticsearch-SQL 支持删除,可以测试下。

TommyLemon commented 1 year ago

@TommyLemon 请问调用js脚本, 要咋弄呀, 我也测一下

用最新代码,然后 Function 表配置(type=1) 属性、Script 表配置具体脚本就行了,前端不需要改动参数。

image
INSERT INTO `Function` VALUES (3,0,0,0,'countArray','int','array','{\"array\": [1, 2, 3]}','获取数组长度。没写调用键值对,会自动补全 \"result()\": \"countArray(array)\"',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(4,0,0,0,'countObject','int','object','{\"object\": {\"key0\": 1, \"key1\": 2}}','获取对象长度。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(5,0,0,1,'isContain','Boolean','array,value','{\"array\": [1, 2, 3], \"value\": 2}','判断是否数组包含值。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(6,0,0,0,'isContainKey','boolean','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','判断是否对象包含键。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(7,0,0,0,'isContainValue','boolean','object,value','{\"value\": 1, \"object\": {\"id\": 1}}','判断是否对象包含值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(8,0,0,0,'getFromArray','Object','array,position','{\"array\": [1, 2, 3], \"result()\": \"getFromArray(array,1)\"}','根据下标获取数组里的值。position 传数字时直接作为值,而不是从所在对象 request 中取值',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(9,0,0,0,'getFromObject','Object','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','根据键获取对象里的值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(10,0,0,0,'deleteCommentOfMoment','int','momentId','{\"momentId\": 1}','根据动态 id 删除它的所有评论',0,'Moment','DELETE','2019-08-17 18:46:56',NULL),(11,0,0,0,'verifyIdList',NULL,'array','{\"array\": [1, 2, 3], \"result()\": \"verifyIdList(array)\"}','校验类型为 id 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(12,0,0,0,'verifyURLList',NULL,'array','{\"array\": [\"http://123.com/1.jpg\", \"http://123.com/a.png\", \"http://www.abc.com/test.gif\"], \"result()\": \"verifyURLList(array)\"}','校验类型为 URL 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(13,0,0,0,'getWithDefault','Object','value,defaultValue','{\"value\": null, \"defaultValue\": 1}','如果 value 为 null,则返回 defaultValue',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(14,0,0,0,'removeKey','Object','key','{\"key\": \"s\", \"key2\": 2}','从对象里移除 key',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(15,0,0,0,'getFunctionDemo','JSONObject',NULL,'{}','获取远程函数的 Demo',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(16,0,0,0,'getFunctionDetail','String',NULL,'{}','获取远程函数的详情',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(17,0,0,0,'getMethodArguments','String','methodArgs','{\"methodArgs\": \"methodArgs\"}','获取远程函数的参数',0,NULL,NULL,'2021-07-29 09:32:22',NULL),(18,0,0,0,'getMethodDefination','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL),(19,0,0,0,'getMethodRequest','String',NULL,'{}','获取远程函数的请求',0,NULL,NULL,'2021-07-29 09:35:37',NULL),(20,0,0,0,'deleteChildComment','int','commentId','{}','删除评论的子评论',0,NULL,NULL,'2021-09-10 06:53:24',NULL),(21,0,0,0,'getCurrentUserId','Long',NULL,'{}','获取当前登录用户 id',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(22,0,0,0,'getCurrentUserIdAsList','List',NULL,'{}','获取当前登录用户 id 列表,只包含一个 id,只是为了前端方便构造某些请求',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(24,0,0,0,'getCurrentUser','Visitor',NULL,'{}','获取当前登录用户公开信息',0,NULL,NULL,'2022-02-19 17:34:58',NULL),(26,0,0,0,'getCurrentContactIdList','List',NULL,'{}','获取当前登录用户的联系人 id 列表',0,NULL,NULL,'2022-02-19 17:37:35',NULL),(27,0,0,1,'getType','String','val','{\"val\": 1}','获取类型',0,NULL,NULL,'2022-11-16 16:05:41',NULL),(28,0,0,1,'length','Integer','val','{\"val\": [1, 2, 3]}','获取长度',0,NULL,NULL,'2022-11-16 17:21:53',NULL),(29,0,0,0,'getMethodDefinition','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL);
image
INSERT INTO `Script` VALUES (1,0,0,0,'getType','function getType(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val instanceof Array ? \"array\" : typeof val;\n}','2022-11-16 16:01:23',0),(2,0,0,0,'isContain','function isContain(curObj, arrKey, valKey) {\n    var arr = curObj == null ? null : curObj[arrKey];\n    var val = curObj == null ? null : curObj[valKey];\n    return arr != null && arr.indexOf(val) >=0;\n}','2022-11-16 16:02:48',0),(3,0,0,1,'init','var i = 1;\n\"init done \"  + i;','2022-11-16 16:41:35',0),(4,0,0,0,'length','function length(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val == null ? 0 : val.length;\n}','2022-11-16 17:18:43',0);
TommyLemon commented 1 year ago

网络有问题,刚发的评论没了,我再试试。

DROP TABLE IF EXISTS `Function`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `Function` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `debug` tinyint NOT NULL DEFAULT '0' COMMENT '是否为 DEBUG 调试数据,只允许在开发环境使用,测试和线上环境禁用:0-否,1-是。',
  `userId` bigint NOT NULL COMMENT '管理员用户Id',
  `type` tinyint NOT NULL DEFAULT '0' COMMENT '类型:0-远程函数;1-JavaScript 函数',
  `name` varchar(50) NOT NULL COMMENT '方法名',
  `returnType` varchar(50) DEFAULT 'Object' COMMENT '返回值类型。TODO RemoteFunction 校验 type 和 back',
  `arguments` varchar(100) DEFAULT NULL COMMENT '参数列表,每个参数的类型都是 String。\n用 , 分割的字符串 比 [JSONArray] 更好,例如 array,item ,更直观,还方便拼接函数。',
  `demo` json NOT NULL COMMENT '可用的示例。\nTODO 改成 call,和返回值示例 back 对应。',
  `detail` varchar(1000) NOT NULL COMMENT '详细描述',
  `version` tinyint NOT NULL DEFAULT '0' COMMENT '允许的最低版本号,只限于GET,HEAD外的操作方法。\nTODO 使用 requestIdList 替代 version,tag,methods',
  `tag` varchar(20) DEFAULT NULL COMMENT '允许的标签.\nnull - 允许全部\nTODO 使用 requestIdList 替代 version,tag,methods',
  `methods` varchar(50) DEFAULT NULL COMMENT '允许的操作方法。\nnull - 允许全部\nTODO 使用 requestIdList 替代 version,tag,methods',
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `return` varchar(45) DEFAULT NULL COMMENT '返回值示例',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb3 COMMENT='远程函数。强制在启动时校验所有demo是否能正常运行通过';
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `Function` VALUES (3,0,0,0,'countArray','int','array','{\"array\": [1, 2, 3]}','获取数组长度。没写调用键值对,会自动补全 \"result()\": \"countArray(array)\"',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(4,0,0,0,'countObject','int','object','{\"object\": {\"key0\": 1, \"key1\": 2}}','获取对象长度。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(5,0,0,1,'isContain','Boolean','array,value','{\"array\": [1, 2, 3], \"value\": 2}','判断是否数组包含值。',0,NULL,NULL,'2018-10-13 08:23:23',NULL),(6,0,0,0,'isContainKey','boolean','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','判断是否对象包含键。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(7,0,0,0,'isContainValue','boolean','object,value','{\"value\": 1, \"object\": {\"id\": 1}}','判断是否对象包含值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(8,0,0,0,'getFromArray','Object','array,position','{\"array\": [1, 2, 3], \"result()\": \"getFromArray(array,1)\"}','根据下标获取数组里的值。position 传数字时直接作为值,而不是从所在对象 request 中取值',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(9,0,0,0,'getFromObject','Object','object,key','{\"key\": \"id\", \"object\": {\"id\": 1}}','根据键获取对象里的值。',0,NULL,NULL,'2018-10-13 08:30:31',NULL),(10,0,0,0,'deleteCommentOfMoment','int','momentId','{\"momentId\": 1}','根据动态 id 删除它的所有评论',0,'Moment','DELETE','2019-08-17 18:46:56',NULL),(11,0,0,0,'verifyIdList',NULL,'array','{\"array\": [1, 2, 3], \"result()\": \"verifyIdList(array)\"}','校验类型为 id 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(12,0,0,0,'verifyURLList',NULL,'array','{\"array\": [\"http://123.com/1.jpg\", \"http://123.com/a.png\", \"http://www.abc.com/test.gif\"], \"result()\": \"verifyURLList(array)\"}','校验类型为 URL 列表',0,NULL,NULL,'2019-08-17 19:58:33',NULL),(13,0,0,0,'getWithDefault','Object','value,defaultValue','{\"value\": null, \"defaultValue\": 1}','如果 value 为 null,则返回 defaultValue',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(14,0,0,0,'removeKey','Object','key','{\"key\": \"s\", \"key2\": 2}','从对象里移除 key',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(15,0,0,0,'getFunctionDemo','JSONObject',NULL,'{}','获取远程函数的 Demo',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(16,0,0,0,'getFunctionDetail','String',NULL,'{}','获取远程函数的详情',0,NULL,NULL,'2019-08-20 15:26:36',NULL),(17,0,0,0,'getMethodArguments','String','methodArgs','{\"methodArgs\": \"methodArgs\"}','获取远程函数的参数',0,NULL,NULL,'2021-07-29 09:32:22',NULL),(18,0,0,0,'getMethodDefination','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL),(19,0,0,0,'getMethodRequest','String',NULL,'{}','获取远程函数的请求',0,NULL,NULL,'2021-07-29 09:35:37',NULL),(20,0,0,0,'deleteChildComment','int','commentId','{}','删除评论的子评论',0,NULL,NULL,'2021-09-10 06:53:24',NULL),(21,0,0,0,'getCurrentUserId','Long',NULL,'{}','获取当前登录用户 id',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(22,0,0,0,'getCurrentUserIdAsList','List',NULL,'{}','获取当前登录用户 id 列表,只包含一个 id,只是为了前端方便构造某些请求',0,NULL,NULL,'2022-02-19 17:27:41',NULL),(24,0,0,0,'getCurrentUser','Visitor',NULL,'{}','获取当前登录用户公开信息',0,NULL,NULL,'2022-02-19 17:34:58',NULL),(26,0,0,0,'getCurrentContactIdList','List',NULL,'{}','获取当前登录用户的联系人 id 列表',0,NULL,NULL,'2022-02-19 17:37:35',NULL),(27,0,0,1,'getType','String','val','{\"val\": 1}','获取类型',0,NULL,NULL,'2022-11-16 16:05:41',NULL),(28,0,0,1,'length','Integer','val','{\"val\": [1, 2, 3]}','获取长度',0,NULL,NULL,'2022-11-16 17:21:53',NULL),(29,0,0,0,'getMethodDefinition','String','method,arguments,type,exceptions,language','{\"method\": \"method\"}','获取远程函数的签名定义',0,NULL,NULL,'2021-07-29 09:34:37',NULL);
TommyLemon commented 1 year ago
DROP TABLE IF EXISTS `Script`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `Script` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `documentId` bigint NOT NULL,
  `randomId` bigint NOT NULL,
  `simple` tinyint NOT NULL DEFAULT '0' COMMENT '是否为可直接执行的简单代码段:0-否 1-是',
  `name` varchar(100) NOT NULL,
  `script` text NOT NULL,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ahead` tinyint NOT NULL DEFAULT '0' COMMENT '是否为前置脚本',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COMMENT='脚本,前置预处理脚本、后置断言和恢复脚本等';
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `Script` VALUES (1,0,0,0,'getType','function getType(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val instanceof Array ? \"array\" : typeof val;\n}','2022-11-16 16:01:23',0),(2,0,0,0,'isContain','function isContain(curObj, arrKey, valKey) {\n    var arr = curObj == null ? null : curObj[arrKey];\n    var val = curObj == null ? null : curObj[valKey];\n    return arr != null && arr.indexOf(val) >=0;\n}','2022-11-16 16:02:48',0),(3,0,0,1,'init','var i = 1;\n\"init done \"  + i;','2022-11-16 16:41:35',0),(4,0,0,0,'length','function length(curObj, key) {\n    var val = curObj == null ? null : curObj[key];\n    return val == null ? 0 : val.length;\n}','2022-11-16 17:18:43',0);
cloudAndMonkey commented 1 year ago

@TommyLemon Elasticsearch-SQL 支持删除,可以测试下。 不支持呀 我用_xpack、https://github.com/NLPchina/elasticsearch-sql 都测了

不支持 delete image

TommyLemon commented 1 year ago

这样啊,看来和文档有出入 https://github.com/NLPchina/elasticsearch-sql#sql-usage

image

是不是版本号太低了? https://github.com/NLPchina/elasticsearch-sql#install-as-pluginversions

image
TommyLemon commented 1 year ago

可以先把目前实现查询的代码改动提 PR 贡献下哦

cloudAndMonkey commented 1 year ago

这样啊,看来和文档有出入 https://github.com/NLPchina/elasticsearch-sql#sql-usage image

是不是版本号太低了? https://github.com/NLPchina/elasticsearch-sql#install-as-pluginversions image

不支持,我用的elasticSearch7.17.5 我看源码是通过 org.elasticsearch.index.reindex.DeleteByQueryRequest 实现删除的, 我调用他的 单元测试, 删除还是不行, 我后面再测一下

cloudAndMonkey commented 1 year ago

可以先把目前实现查询的代码改动提 PR 贡献下哦

我还没有测好, 还有一些兼容的地方,我还没改完

TommyLemon commented 1 year ago

这样啊,期待

cloudAndMonkey commented 1 year ago

并且它的delete并不是jdbc的方式:

这样啊,看来和文档有出入 https://github.com/NLPchina/elasticsearch-sql#sql-usage image 是不是版本号太低了? https://github.com/NLPchina/elasticsearch-sql#install-as-pluginversions image

不支持,我用的elasticSearch7.17.5 我看源码是通过 org.elasticsearch.index.reindex.DeleteByQueryRequest 实现删除的, 我调用他的 单元测试, 删除还是不行, 我后面再测一下

@TommyLemon 1、elasticSearch-sql delete通了, 他是二次封装, 并不是用 jdbc方式调用. 调用方式如下: image

2、 (非jdbc)新增index image

2、 (非jdbc)支持数据新增(导入方式) image

3、 (非jdbc)新增mappinng image

4、修改(不支持) 我空了可以研究一下

cloudAndMonkey commented 1 year ago

@TommyLemon 1、java 连接 Elasticsearch 使用 TransportClient 进行插入、删除、修改、查询数据: https://blog.csdn.net/m0_50163856/article/details/119894246 elasticSearch 实现 delete原理: 将sql 转换为 TransportClient 语句执行 String deleteStatement = "delete from " + TEST_INDEX +" where title.keyword = \"Linux安装\""; 估计作者觉得不如直接操作 TransportClient 方便, 只实现了 delete, insert、update没有实现

elastic-sql sql语句查询不需要转换, 可以直接发给elasticsearch执行 POST /_xpack/sql?format=txt { "query": "select sum(price) from products " }

POST /_sql?format=txt { "query":"EXPLAIN es_blog" }

cloudAndMonkey commented 1 year ago

apijson 如果使用 elasticsearch 的非jdbc删除,会进行多次转换 json --> jdbc --> sql解析 --> TransportClient 执行语句 --> 执行

cloudAndMonkey commented 1 year ago

@TommyLemon 我记得你让我用elasticSearch数据源 测试远程函数?

cloudAndMonkey commented 1 year ago

@TommyLemon { "ES_blog:a": { "@datasource": "ELASTICSEARCH", "title:test": "Xshell6安装" } } 我偶然测试发现: 字段名 : 分隔,会被截取. 要不要处理一下? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 已生成 1 条 SQL execute startTime = 1670311557672 database = ELASTICSEARCH; schema = ; sql = SELECT * FROM es_blog WHERE ( (test = 'Xshell6安装') ) LIMIT 1

cloudAndMonkey commented 1 year ago

@TommyLemon 我测试了crud jdbc、elasticSearch-sql 操作,没有问题

{
    "@post": {
        "User:aa": {
             "@datasource": "db2"
        },
        "User_address[]": {
             "@datasource": "db2"
        }
    },
    "User:aa":{
        "username":"test-3",
        "password": "233223",
        "state": 1
    },
    "ES_blog:a": {
        "@datasource": "ELASTICSEARCH",
        "title.keyword": "Xshell6安装"
    },
    "User_address[]": [
        {
        "user_id@": "User:aa/id",
        "addr": "ddd",
        "count@": "ES_blog:a/count"
        },
        {
        "user_id@": "User:aa/id",
        "addr": "ddd1",
        "count@": "ES_blog:a/count"
        }
    ]
}

image

elasticsearch-sql 不支持explain. 实现效果: 如果传递explain, 不执行查询,返回sql语句

{
    "ES_blog:a": {
        "@datasource": "ELASTICSEARCH",
        "title.keyword": "Xshell6安装"
    },
    "@explain": true
}

image

cloudAndMonkey commented 1 year ago

1、查询 字段 .keyword支持

"ES_blog:a": {
"@datasource": "ELASTICSEARCH",
"title.keyword": "Xshell6安装"
}

2、index、type支持 不管type也可以,高版本 已经不建议用type了 image

cloudAndMonkey commented 1 year ago

@TommyLemon sql 字段 是预留的吗? image

TommyLemon commented 1 year ago

apijson 如果使用 elasticsearch 的非jdbc删除,会进行多次转换 json --> jdbc --> sql解析 --> TransportClient 执行语句 --> 执行

只要不执行 SQLConfig.getSQL 就不会有 JSON->SQL 的过程,不过对于 Elasticsearch 增删改只有 @explain: true 的时候才 getSQL 用于返回给前端方便调试(查询必须,需要 SQL 作为缓存 key)。 可以重写 AbstractSQLConfig.execute(SQLConfig config, boolean unknownType) 然后判断是 修改/删除 等不能直接传 SQL 给 Elasticsearch 的方法,就改用 TransportClient 的方式。 INSERT 的数据都可以用 config.getValues 获取,UPDATE 的数据都可用 config.getContent 获取,另外 getWhere 获取条件。

TommyLemon commented 1 year ago

apijson 如果使用 elasticsearch 的非jdbc删除,会进行多次转换 json --> jdbc --> sql解析 --> TransportClient 执行语句 --> 执行

这个额外的参数 String sql 是为了减少 getSQL 的重复调用,如果 config.isExplain() == false,则可以复用作为缓存 key 及打印日志而调用 config.getSQL(false) 得到的 SQL 字符串。

@TommyLemon sql 字段 是预留的吗? image

这个额外的参数 String sql 是为了减少 getSQL 的重复调用,如果 config.isExplain() == false,则可以复用作为缓存 key 及打印日志而调用 config.getSQL(false) 得到的 SQL 字符串。

TommyLemon commented 1 year ago

apijson 如果使用 elasticsearch 的非jdbc删除,会进行多次转换 json --> jdbc --> sql解析 --> TransportClient 执行语句 --> 执行

这个额外的参数 String sql 是为了减少 getSQL 的重复调用,如果 config.isExplain() == false,则可以复用作为缓存 key 及打印日志而调用 config.getSQL(false) 得到的 SQL 字符串。

TommyLemon commented 1 year ago

@TommyLemon { "ES_blog:a": { "@Datasource": "ELASTICSEARCH", "title:test": "Xshell6安装" } } 我偶然测试发现: 字段名 : 分隔,会被截取. 要不要处理一下? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 已生成 1 条 SQL execute startTime = 1670311557672 database = ELASTICSEARCH; schema = ; sql = SELECT * FROM es_blog WHERE ( (test = 'Xshell6安装') ) LIMIT 1

应该是用 Pair.parseEntry 解析了 name:alias 用冒号分割的 key,然后只用到了 name 导致。 用 title.keyword 格式更直观,更贴近用户习惯,只是和 apijson-router 用点分割路径中所有字段这个冲突。 https://github.com/APIJSON/apijson-router

image

得想想怎么取舍,或者改用 - 中横线等替代方式。

后续实现多字段关联同一个子查询也考虑用中横线 -,其次是分号 ;

{
  "id-name@": {  // 还有 IN "id-name{}@" 等
    "from": "User",
    "User": {
      "@column":"id,name"
    }
  }
}
WHERE (id,name) = (SELECT id,name FROM User) // WHERE (id,name) IN (SELECT id,name FROM User)

关于新增功能、优化性能等的一些想法 https://github.com/Tencent/APIJSON/issues/37#issuecomment-1293368463

image