Open cloudAndMonkey opened 1 year ago
还好 apijson支持prepare Statement, elastic-sql 不支持statement ,哈哈
@TommyLemon elasticSearch插入成功啦, 单条, 其他情况 我还要多测一下 查询插入的数据:
昨天我想了好久, 然后把源码流程再梳理了一下. 新增/修改/删除 实现: SQLExecutor public int executeUpdate(@NotNull SQLConfig config, String sql) throws Exception
查询实现: SQLExecutor ResultSet executeQuery(@NotNull SQLConfig config, String sql) throws Exception;
不执行生成sql语句, 和 jdbc sql执行, 😄
elasticSearch不支持事物 目前我们是 一条一条执行, 然后 统一提交 需要 将相同数据源,非jdbc 的语句 进行批量操作吗?
APIJSON 目前是因为要处理 权限、远程函数、子对象,才不得不逐条执行,而不是 INSERT INTO table VALUES(..),(..),(..)... 尽管 AbstractSQLConfig 是支持这种批量提交方式,但在 AbstractObjectParser.onTableArrayParse 还是拆分成了多条。
AbstractObjectParser.onTableArrayParse https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L637-L661
不过只要有 1 条失败,就会整体失败(后续可能在后端加个配置,允许部分失败),所以不用担心不支持事务这个问题。 如果要优化性能,则可以考虑改下源码。
只是实现这个功能不难,关键是要兼容 权限、远程函数、子对象,保证每条都能执行到。 目前也可以类似 AbstractParser.onJoinParse 提前扫描所有子对象,判断是否存在 角色权限、远程函数、子对象,如果都不存在,则不用拆分。 或者先试试直接在 AbstractObjectParser 中判断,这个实现更简单,类似之前对单表批量查询性能的优化。 https://my.oschina.net/tommylemon/blog/5375645
主要改动点在: AbstractObjectParser.onTableArrayParse https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L637-L661
等我忙完,再研究一下
赞,我更新了以上回复,目前可以不用管的,只要有 1 条 INSERT/UPDATE 失败就整体失败
@TommyLemon 请问调用js脚本, 要咋弄呀, 我也测一下
用最新代码,然后 Function 表配置(type=1) 属性、Script 表配置具体脚本就行了,前端不需要改动参数。
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);
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);
远程函数:支持 JavaScript 外的更多脚本语言,例如 Python, Ruby, Lua, PHP 等 https://github.com/Tencent/APIJSON/commit/dd372797e30bff6db705eee550b63e28b92a0f1a
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',
`language` varchar(45) DEFAULT NULL COMMENT '语言:Java(java), JavaScript(js), Lua(lua), Python(py), Ruby(ruby), PHP(php) 等,NULL 默认为 Java,JDK 1.6-11 默认支持 JavaScript,JDK 12+ 需要额外依赖 Nashron/Rhiro 等 js 引擎库,其它的语言需要依赖对应的引擎库,并在 ScriptEngineManager 中注册',
`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);
js 的 Array 是用 Object 实现的,key 为下标 index,用 typeof arr 得到的不是 array 而是 object 😂
我还没开始测远程函数呢,哈哈
elasticSearch非sql方式, update、delete实现了sql方式操作elasticSearch 通过elasticSearch explain将sql语句转换为 dsl 字符串. elasticSearch代码执行 dsl字符串 如果不转换, 需要解析where条件(支持id/非id, 模糊匹配等等),生成elasticSearch 功能实现代码
@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
得想想怎么取舍,或者改用
-
中横线等替代方式。后续实现多字段关联同一个子查询也考虑用中横线 -,其次是分号 ;
{ "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)
关于新增功能、优化性能等的一些想法 #37 (comment)
现在看应该是 "title-keyword" 这种用中横线的格式更适合作为递进层级的 key,替代 title.keyword,类似 "User-id[]":{} 提取字段也是 id 从属 User 这种递进关系; https://github.com/Tencent/APIJSON/blob/master/Document.md#3.2
另外多个字段子查询则使用分号 ; 隔开,例如
{
"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)
不能用逗号 , ,因为会和现有的 join 冲突
"join":"/User/id,name@"
会被分拆成 /User/id 和 name@ ,当成 JOIN 两张副表
elasticSearch非sql方式, update、delete实现了sql方式操作elasticSearch 通过elasticSearch explain将sql语句转换为 dsl 字符串. elasticSearch代码执行 dsl字符串 如果不转换, 需要解析where条件(支持id/非id, 模糊匹配等等),生成elasticSearch 功能实现代码
既然 Elasticsearch 最核心的查询已经实现了,我看可以先给 apijson-framework 提交 PR ,或者开源一个叫 apijson-elasticsearch 的插件,可能很多用户对于未实现的功能不是刚需(不一定通过 APIJSON JSON 协议实现,可以有 远程函数 或 新增接口单独调用 Elasticsearch RESTful API 等多种替代方式),后面再逐步完善
@TommyLemon 请问调用js脚本, 要咋弄呀, 我也测一下
用最新代码,然后 Function 表配置(type=1) 属性、Script 表配置具体脚本就行了,前端不需要改动参数。
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);
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);
远程函数:支持 JavaScript 外的更多脚本语言,例如 Python, Ruby, Lua, PHP 等 dd37279
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', `language` varchar(45) DEFAULT NULL COMMENT '语言:Java(java), JavaScript(js), Lua(lua), Python(py), Ruby(ruby), PHP(php) 等,NULL 默认为 Java,JDK 1.6-11 默认支持 JavaScript,JDK 12+ 需要额外依赖 Nashron/Rhiro 等 js 引擎库,其它的语言需要依赖对应的引擎库,并在 ScriptEngineManager 中注册', `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);
js 的 Array 是用 Object 实现的,key 为下标 index,用 typeof arr 得到的不是 array 而是 object 😂
目前对于脚本格式的远程函数,实现有个 NPE 问题: Invocable invocable = engine instanceof Invocable ? (Invocable) engine : null; 为 null 时执行 invocable.invokeFunction 会抛 NullPointerException,得想想不是什么情况下 engine 不属于 Invocable,如果不属于,以什么方式来替代 Invocable.invokeFunction 方法,能够执行脚本里的 方法/函数 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L360-L364
elasticSearch非sql方式, update、delete实现了sql方式操作elasticSearch 通过elasticSearch explain将sql语句转换为 dsl 字符串. elasticSearch代码执行 dsl字符串 如果不转换, 需要解析where条件(支持id/非id, 模糊匹配等等),生成elasticSearch 功能实现代码
既然 Elasticsearch 最核心的查询已经实现了,我看可以先给 apijson-framework 提交 PR ,或者开源一个叫 apijson-elasticsearch 的插件,可能很多用户对于未实现的功能不是刚需(不一定通过 APIJSON JSON 协议实现,可以有 远程函数 或 新增接口单独调用 Elasticsearch RESTful API 等多种替代方式),后面再逐步完善 @TommyLemon update、delete最终解决方案(个人理解): 1、id /id{} update、delete 这个简单 2、条件 update、delete 1) 重用 apijson sql解析 生成elasticSearch语句,导入 elasticSearch api 执行(重写executeUpdate即可) 需要解决语法兼容等, 感觉还是通过sql语句,让 elasticSearch去生成dsl简单. 只是多了一次转换和远程调用 2) 调用 elasticSearch sql explain 生成 query dsl string, 将 修改字段和 query 导入 elasticSearch api 执行
我想实现apijson配置就能自动执行,不需要关心 elasticSearch语法. 哈哈😄 我稍微再完善一下
搞定啦, 哈哈 update 和 delete 按照elasticsearch-sql 一样的流程执行啦 将sql解析为 elasticSearch api 执行
感觉可以稍微调整一下elasticSearch-sql 源码, apijson就能无缝集成,同时支持查询、新增、修改、删除 1、apijson支持操作类型method 2、apijson能通过method 控制 statement
使用x-pack 收费版, 更简单, 哈哈
赞,我更新了以上回复,目前可以不用管的,只要有 1 条 INSERT/UPDATE 失败就整体失败
我详细看了你的意思, 我也觉得先不用管
@TommyLemon 我把多数据源、elasticSearch 回归测试,搞完善再提交pr哈
多数据源 子查询和主语句使用一个数据源 ok join, UNION 还没加 SQL 子查询, SQL 主表 JOIN 副表 最终都是生成和执行一条 SQL,必须在同一个 connection 中(@datasource, @database, method, @role 当然也必须相同)。
elasticSearch回归拉通测试
非jdbc, 不支持 事物 apijson程序setAutoCommit 不处理哦 我们设置conncetion setAutoCommit 没有意义 4、elasticSearch title.keywrod 这次需要调整吗? 5、 update、delete 对象不存在, 没有更新、删除成功的情况 json全局配置参数来控制比较合理,比如:
1、JOIN, UNION 可以等提交 PR 后再搞 看起来 Elasticseach 不支持 SQL JOIN 功能,搜到的 JOIN 都支持合并数据而不是 SQL JOIN 这种联表查询,实现应该比较麻烦。 https://www.elastic.co/cn/search?q=JOIN&size=n_20_n&filters%5B0%5D%5Bfield%5D=product_name&filters%5B0%5D%5Bvalues%5D%5B0%5D=Elasticsearch&filters%5B0%5D%5Btype%5D=all https://www.elastic.co/guide/en/elasticsearch/reference/current/joining-queries.html
https://rockset.com/blog/how-to-join-data-in-elasticsearch-vs-rockset/ http://leapfire.com/elasticsearchjoin.html
3、既然它不支持事务的话只能暂时不处理事务 4、可以先提交代码后再调整 5、这个确实可以加个参数,但应该由后端控制,不能让前端决定事务,避免造成数据不一致等问题
@TommyLemon 我有个疑问 web系统, json从客户端网页 传递到服务端, 网页改变了 json,不就改变了流程? 请问你们一般怎么处理的? 如果是单条sql 操作, 还影响不大
@TommyLemon elasticsearch-sql不更新了, 并且elasticsearch8 不支持 TransportClient 可以参考: https://github.com/opendistro-for-elasticsearch/sql/blob/main/sql-jdbc/src/test/java/com/amazon/opendistroforelasticsearch/jdbc/ResultSetTests.java 这是亚马逊 用开源 elasticsearch 搞的免费版
@TommyLemon 我有个疑问 web系统, json从客户端网页 传递到服务端, 网页改变了 json,不就改变了流程? 请问你们一般怎么处理的? 如果是单条sql 操作, 还影响不大
这就是为啥增删改要通过 Request 表配置校验规则(包括必须执行的远程函数等),让前端(客户端)必须按规则来传参 https://github.com/APIJSON/apijson_todo_demo/blob/master/FULLTEXT.md#%E8%AE%BF%E9%97%AE%E6%96%B9%E6%B3%95%E8%AF%B7%E6%B1%82%E4%BD%93%E4%B8%8E-request
@TommyLemon 我有个疑问 web系统, json从客户端网页 传递到服务端, 网页改变了 json,不就改变了流程? 请问你们一般怎么处理的? 如果是单条sql 操作, 还影响不大
这就是为啥增删改要通过 Request 表配置校验规则(包括必须执行的远程函数等),让前端(客户端)必须按规则来传参 https://github.com/APIJSON/apijson_todo_demo/blob/master/FULLTEXT.md#%E8%AE%BF%E9%97%AE%E6%96%B9%E6%B3%95%E8%AF%B7%E6%B1%82%E4%BD%93%E4%B8%8E-request
request只能控制单条语句的校验, crud 包含多条语句
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
@TommyLemon 多数据源 SQL 子查询、join、子查询+join、union(不影响, apijosn是解析成单条语句执行, 然后对查询结果数据拼接) SQL 主表 JOIN 副表 最终都是生成和执行一条 SQL,必须在同一个 connection 中(@Datasource, @database, method, @ROLE 当然也必须相同)。
子查询
{
"@get": {
"Sys_user_role": {
},
"Sys_role_permission": {
}
},
"sql@": {
"with": true,
"from": "Sys_role",
"Sys_role": {
"@datasource": "db2",
"@column": "id",
"role_name": "超级管理员"
}
},
"sql_user@": {
"with": true,
"from": "Sys_user",
"Sys_user": {
"@datasource": "db2",
"@column": "id",
"id": "4732209c-5785-4827-b532-5092f154fd94"
}
},
"Sys_user_role:sur[]": {
"Sys_user_role": {
"@datasource": "db1",
"role_id{}@": "sql",
"user_id{}@": "sql_user"
}
},
"Sys_role_permission:srp[]": {
"Sys_role_permission": {
"@datasource": "db2",
"role_id{}@": "sql"
}
},
"@explain": true
}
join
{
"Sys_permission[]": {
"Sys_permission": {
"@from@": {
"from": "Sys_user_role",
"join": "&/Sys_role/id@,&/Sys_role_permission/role_id@,&/Sys_permission/id@",
"Sys_user_role": {
"user_id": "4732209c-5785-4827-b532-5092f154fd94",
"@column": ""
},
"Sys_role": {
"@datasource": "155db",
"@column": "id:role_id,role_name",
"id@": "/Sys_user_role/role_id"
},
"Sys_role_permission": {
"@datasource": "155db",
"@column": "",
"role_id@": "/Sys_role/id"
},
"Sys_permission": {
"@datasource": "155db",
"@column": "id:permission_id,name:permission_name",
"id@": "/Sys_role_permission/permission_id"
}
}
}
},
"@explain": true
}
3、join + 子查询
{
//"@datasource": "155db",
"@get": {
"Sys_user_role": {
},
"Sys_role_permission": {
}
},"sql@": {
"with": true,
"from": "Sys_role",
"Sys_role": {
"@datasource": "db2",
"@column": "id",
"role_name": "超级管理员"
}
},"Sys_permission[]": {
"Sys_permission": {
"@from@": {
"from": "Sys_user_role",
"join": "&/Sys_role/id@,&/Sys_role_permission/role_id@,&/Sys_permission/id@",
"Sys_user_role": {
"@datasource": "155db",
"user_id": "4732209c-5785-4827-b532-5092f154fd94",
"role_id{}@": "sql",
"@column": ""
},
"Sys_role": {
"@datasource": "155db",
"@column": "id:role_id,role_name",
"id@": "/Sys_user_role/role_id"
},
"Sys_role_permission": {
"@datasource": "db1",
"@column": "",
"role_id@": "/Sys_role/id"
},
"Sys_permission": {
"@datasource": "db2",
"@column": "id:permission_id,name:permission_name",
"id@": "/Sys_role_permission/permission_id"
}
}
}
},
"@explain": true
}
4、union 程序是把多条语句拆成单条语句执行,不用做数据源合并
{
"Sys_user:su": {
"@datasource": "master",
"@column": "count(id):total",
"username": "normalTest"
},
"Sys_user:su2": {
"@datasource": "155db",
"@column": "count(id):total",
"username": "323232"
}
}
没改几行代码, 主要是要理解业务执行逻辑,理了我两个小时, join、子查询解析有代码重叠:
我开始合并代码,提交pr,哈哈 elasticSearch-sql支持, 很完善了, 只要elasticSearch-sql支持的功能, apijson都能用 修改、删除也沿用了 elasticsearch-sql的解析. 学会apijson语法, 写好json即可,不用关心elasticsearch语法啦
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
这个等我提完代码再跟进,详细梳理一下
redis sql集成: rediSQL 高版本也可以使用, 通过sql语句操作redis数据库 也支持虚拟表, 映射 redis hashmap,比如: https://redisql.redbeardlab.com/references/#redisql_tables_brute_hash redis cluster能不能安装,我要再测一下, 它也有点贱, 它搞了一个遥测函数,定时把redis statistics 定时发送给他的服务器, 如果去掉遥测, 要给钱,买pro版本,😄 https://github.com/RedBeardLab/rediSQL/blob/master/telemetrics/src/lib.rs
@TommyLemon 多数据源 SQL 子查询、join、子查询+join、union(不影响, apijosn是解析成单条语句执行, 然后对查询结果数据拼接) SQL 主表 JOIN 副表 最终都是生成和执行一条 SQL,必须在同一个 connection 中(@Datasource, @database, method, @ROLE 当然也必须相同)。
- 子查询
{ "@get": { "Sys_user_role": { }, "Sys_role_permission": { } }, "sql@": { "with": true, "from": "Sys_role", "Sys_role": { "@datasource": "db2", "@column": "id", "role_name": "超级管理员" } }, "sql_user@": { "with": true, "from": "Sys_user", "Sys_user": { "@datasource": "db2", "@column": "id", "id": "4732209c-5785-4827-b532-5092f154fd94" } }, "Sys_user_role:sur[]": { "Sys_user_role": { "@datasource": "db1", "role_id{}@": "sql", "user_id{}@": "sql_user" } }, "Sys_role_permission:srp[]": { "Sys_role_permission": { "@datasource": "db2", "role_id{}@": "sql" } }, "@explain": true }
- join
{ "Sys_permission[]": { "Sys_permission": { "@from@": { "from": "Sys_user_role", "join": "&/Sys_role/id@,&/Sys_role_permission/role_id@,&/Sys_permission/id@", "Sys_user_role": { "user_id": "4732209c-5785-4827-b532-5092f154fd94", "@column": "" }, "Sys_role": { "@datasource": "155db", "@column": "id:role_id,role_name", "id@": "/Sys_user_role/role_id" }, "Sys_role_permission": { "@datasource": "155db", "@column": "", "role_id@": "/Sys_role/id" }, "Sys_permission": { "@datasource": "155db", "@column": "id:permission_id,name:permission_name", "id@": "/Sys_role_permission/permission_id" } } } }, "@explain": true }
3、join + 子查询
{ //"@datasource": "155db", "@get": { "Sys_user_role": { }, "Sys_role_permission": { } },"sql@": { "with": true, "from": "Sys_role", "Sys_role": { "@datasource": "db2", "@column": "id", "role_name": "超级管理员" } },"Sys_permission[]": { "Sys_permission": { "@from@": { "from": "Sys_user_role", "join": "&/Sys_role/id@,&/Sys_role_permission/role_id@,&/Sys_permission/id@", "Sys_user_role": { "@datasource": "155db", "user_id": "4732209c-5785-4827-b532-5092f154fd94", "role_id{}@": "sql", "@column": "" }, "Sys_role": { "@datasource": "155db", "@column": "id:role_id,role_name", "id@": "/Sys_user_role/role_id" }, "Sys_role_permission": { "@datasource": "db1", "@column": "", "role_id@": "/Sys_role/id" }, "Sys_permission": { "@datasource": "db2", "@column": "id:permission_id,name:permission_name", "id@": "/Sys_role_permission/permission_id" } } } }, "@explain": true }
4、union 程序是把多条语句拆成单条语句执行,不用做数据源合并
{ "Sys_user:su": { "@datasource": "master", "@column": "count(id):total", "username": "normalTest" }, "Sys_user:su2": { "@datasource": "155db", "@column": "count(id):total", "username": "323232" } }
没改几行代码, 主要是要理解业务执行逻辑,理了我两个小时, join、子查询解析有代码重叠:
4、union 两个 count 返回的都是单条数据,用不上 UNION。 但如果是 SELECT */字段名 返回的数据有多条,APIJSON 目前只能分别查询,合并只能靠重写方法或远程函数
"User[]":{},
"User:2[]":{},
"list()":"merge(User[],User:2[],true)" // public JSONArray merge(JSONObject curObj, JSONArray arr1, JSONArray arr2, boolean unique)
如果需要在一条 SQL 中处理,这样就不行了,必须支持 SQL 中的 UNION 和 UNION ALL
https://github.com/Tencent/APIJSON/blob/master/Roadmap.md#%E6%96%B0%E5%A2%9E%E6%94%AF%E6%8C%81-union
这个功能可以先提交 PR 后再考虑。
这个功能确实需要
{ "[]": { "User": { "name~": "a", "tag~": "a", "@combine": "name~,tag~", "@union": 1 //将 @combine 中的 N 个 OR 连接字段用 UNION 替换,原本一条 SQL 需要拆分成 N 条 SQL 来 UNION } }, "@explain": true }
@TommyLemon
按照上面的写法, 不方便理解,是否沿用 join查询(支持相同表和不同表union)
{
"Sys_permission[]": {
"Sys_permission": {
"@from@": {
"from": "Sys_user_role",
"union": "xxx1,xxx2,xxx3",
}
}
},
"@explain": true
}
如下写法,数据库支持, 底层直接转换为 union即可,减少用户学习成本
{
"[]": {
"User": {
"name~": "a",
"tag~": "a",
"@combine": "name~,tag~", //将 @combine 中的 N 个 OR 连接字段用 UNION 替换,原本一条 SQL 需要拆分成 N 条 SQL 来 UNION
}
},
"@explain": true
}
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
3. 如下写法,数据库支持, 底层直接转换为 union即可,减少用户学习成本
1.不同表 UNION 会导致很多 NULL 值(部分是主表所有字段为 NULL,部分是副表所有字段为 NULL),目前除了水平拆分表,还没看到其它对应的需求场景
2.部分数据库的较新版本支持,所以其实也不急于实现
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
APIJSON 请求参数 JSON 是有序的,除了关键词,执行顺序都就是按传参的键值对顺序来的。 目前你发现在哪些场景满足不了流程控制的需求呢?
我头疼得要死,好像新冠了,等我好了, elasticsearch-sql demo合并提交
这样啊,先不急着贡献,好好休息,严重就及时就医,祝早日康复哈~
新冠肺炎检测结果呈现阳性及安全地居家管理新冠肺炎 https://www.health.nsw.gov.au/Infectious/covid-19/Documents/translated/confirmed-cases-cs.pdf
新型冠状病毒肺炎(COVID-19)的居家管理:照护者指南 https://www.mskcc.org/zh-hans/cancer-care/patient-education/managing-covid-19-home-information-caregivers
感染新冠病毒“吃药顺序图”靠谱么?专家重要提醒 https://www.suzhou.gov.cn/szsrmzf/mszx/202212/5858911e74084131b3e646c10ab08611.shtml
@TommyLemon 非id条件修改,这里判断有问题. 修改成功多条记录,这是正常情况. return 0 // 一条都未修改成功, 配置 ALLOW_PARTIAL_UPDATE_FAILED 不抛出异常,继续执行 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L696-L710
比如: { "User:aa[]": [ { "username~": "test-3", "password": "1111" } ], "tag": "User[]", "explain": true } 修改成功3条
cfg.setTable(childKey); // Request 表 structure 中配置 "ALLOW_PARTIAL_UPDATE_FAILED": "Table[],key[],key:alias[]" 自动配置 boolean allowPartialFailed = cfg.allowPartialUpdateFailed();
我本地测试,没有 自动配置呀, 哈哈
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
APIJSON 请求参数 JSON 是有序的,除了关键词,执行顺序都就是按传参的键值对顺序来的。 目前你发现在哪些场景满足不了流程控制的需求呢?
我想做服务编排,就像工作流引擎: 条件判断A: A=1 : 执行 a、b A=2: 执行b、c 我在外层做就好了,具体业务场景 等后面具体补充
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息
按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验
我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路: 1) 后端页面配置生成crud模版 2) 用户页面填写数据 3) 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定 4) 调用apijson crud方法
@TommyLemon 非id条件修改,这里判断有问题. 修改成功多条记录,这是正常情况. return 0 // 一条都未修改成功, 配置 ALLOW_PARTIAL_UPDATE_FAILED 不抛出异常,继续执行 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L696-L710
比如: { "User:aa[]": [ { "username~": "test-3", "password": "1111" } ], "tag": "User[]", "explain": true } 修改成功3条
cfg.setTable(childKey); // Request 表 structure 中配置 "ALLOW_PARTIAL_UPDATE_FAILED": "Table[],key[],key:alias[]" 自动配置 boolean allowPartialFailed = cfg.allowPartialUpdateFailed();
我本地测试,没有 自动配置呀, 哈哈
确实哦,只考虑了传 id,没考虑到最近你新增的非 id 情况,这里 count 得根据之前加的 AbstractVerifier.IS_UPDATE_MUST_HAVE_ID_CONDITION 来判断,如果是 false,则根据实际返回的 count 来,true 则按 1 来。
ALLOW_PARTIAL_UPDATE_FAILED 是在数组子项数量在 2 以上,有 1 个以上失败,但至少有 1 个成功,才允许通过。
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
APIJSON 请求参数 JSON 是有序的,除了关键词,执行顺序都就是按传参的键值对顺序来的。 目前你发现在哪些场景满足不了流程控制的需求呢?
我想做服务编排,就像工作流引擎: 条件判断A: A=1 : 执行 a、b A=2: 执行b、c 我在外层做就好了,具体业务场景 等后面具体补充
这样啊,这种逻辑编排目前放在外层做确实更好,也有各种开源的(Airflow, LiteFlow)、商业的流程引擎,包括腾讯问卷这种拖拽式的流程图方式来处理 if-else 分支等。 APIJSON ORM 没必要加上这个 ORM 不是强相关的功能,如果要扩展也是通过插件等方式。
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
APIJSON 请求参数 JSON 是有序的,除了关键词,执行顺序都就是按传参的键值对顺序来的。 目前你发现在哪些场景满足不了流程控制的需求呢?
我想做服务编排,就像工作流引擎: 条件判断A: A=1 : 执行 a、b A=2: 执行b、c 我在外层做就好了,具体业务场景 等后面具体补充
这样啊,这种逻辑编排目前放在外层做确实更好,也有各种开源的(Airflow, LiteFlow)、商业的流程引擎,包括腾讯问卷这种拖拽式的流程图方式来处理 if-else 分支等。 APIJSON ORM 没必要加上这个 ORM 不是强相关的功能,如果要扩展也是通过插件等方式。
嗯嗯
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息
按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验
我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路:
- 后端页面配置生成crud模版
- 用户页面填写数据
- 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定
- 调用apijson crud方法
@TommyLemon 请问上面我的理解有没有问题呀
调用一个函数(javascript、lua 、java函数) 通过返回结果来控制crud执行流程 我想了一下,还是外层来控制吧
APIJSON 请求参数 JSON 是有序的,除了关键词,执行顺序都就是按传参的键值对顺序来的。 目前你发现在哪些场景满足不了流程控制的需求呢?
我想做服务编排,就像工作流引擎: 条件判断A: A=1 : 执行 a、b A=2: 执行b、c 我在外层做就好了,具体业务场景 等后面具体补充
这样啊,这种逻辑编排目前放在外层做确实更好,也有各种开源的(Airflow, LiteFlow)、商业的流程引擎,包括腾讯问卷这种拖拽式的流程图方式来处理 if-else 分支等。 APIJSON ORM 没必要加上这个 ORM 不是强相关的功能,如果要扩展也是通过插件等方式。
或许提供接入流程引擎的 Demo 就行了,例如对接连接池 Druid, HikariCP 就是这样,APIJSON ORM, apijson-framework 都没做任何相关改动。 https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server
这个不难, apijson支持多数据源、crud(增删改查), 添加一个demo, 等后面我弄到这块再加.主要是这块需要页面配合,设计流程模版
这个不难, apijson支持多数据源、crud(增删改查), 添加一个demo, 等后面我弄到这块再加.主要是这块需要页面配合,设计流程模版
是的,赞
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息
按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验
我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路:
- 后端页面配置生成crud模版
- 用户页面填写数据
- 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定
- 调用apijson crud方法
这个可行,有些用 APIJSON 的低代码平台基本就是这个原理。 不过我回答你上面那个问题用 POST register 举例不太合理,因为是单独实现的 /register 接口,不是用万能通用接口。 POST Document, DELETE Moment 这样的就是走万能通用接口,并且涉及多表关联 新增/删除。
不只是单条语句,而是整个请求,你看下 POST register, DELETE Document 等记录,都是两张不同表,至少 2 条 SQL。
@TommyLemon 我看了post register 1、json包含多个对象 2、代码解析每个对象进行校验 3、保存注册用户等信息 按照post register的实现思路, 新增一个 crud json(包含多条语句), 需要 新增后端接口进行json校验 我的场景: 后端页面配置(增删改查表、elasticsearch、rpc、redis等), 用户页面填写数据, 生成crud json, 调用crud方法,执行流程 我的实现思路:
- 后端页面配置生成crud模版
- 用户页面填写数据
- 后端接口 模版+ 数据 => 生成json 减少开发后端接口, 校验交给apijson 函数去搞定
- 调用apijson crud方法
这个可行,有些用 APIJSON 的低代码平台基本就是这个原理。 不过我回答你上面那个问题用 POST register 举例不太合理,因为是单独实现的 /register 接口,不是用万能通用接口。 POST Document, DELETE Moment 这样的就是走万能通用接口,并且涉及多表关联 新增/删除。
好的,谢谢
简单的校验可以直接用现有的 Operation 中校验规则 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/Operation.java
复杂的就写 js/lua 等脚本代码实现远程函数
简单的校验可以直接用现有的 Operation 中校验规则 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/Operation.java
复杂的就写 js/lua 等脚本代码实现远程函数
嗯嗯,是的.
@TommyLemon 有一个点: apijson支持 javascript、lua 前端开发人员不熟悉后端业务、复杂业务逻辑有难度 后端排斥javascript写业务,习惯了java apijson 支持rpc, Java开发人员用java来写业务逻辑, apijson整合
如果有专职的后端人员,那就用他们熟悉的语言,想写 Java 就写 Java,不过不能像脚本语言这么灵活。 如果是全栈开发,很多都是出自前端用 JavaScript 的人,就会倾向于也用 JavaScript 实现后端。 远程函数 也是一种 RPC,你说的 RPC 是指 gRPC, Dubbo 这类提前定义 struct/message 结构体,生成前后端关于结构体和请求相关代码的项目吗? https://github.com/protocolbuffers/protobuf/blob/main/examples/addressbook.proto 这种一般必须提前定义好,没法实现动态逻辑(除非是用脚本语言的版本,动态生成结构体)
Description
@TommyLemon 功能描述: 一个服务编排的伪码流程设计 将伪码转换为 apijson json、前置、后置函数、javascript、redis、elasticSearch等 并且能够mork每一步,执行中间流程,还能查询每个阶段执行的sql语句,及结果.
json格式:
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等功能测试