主题
业务逻辑开发手册
基本概念
服务
服务是业务逻辑的配置单元。 开发页面点击发布后服务开始运行,在服务列表页可以手动停止或重新开启运行中的服务。 服务的配置有基本配置、变量、节点。 服务的基本配置(服务名、描述)在服务列表页修改。 服务的变量、节点在开发页面修改。
类型系统
逻辑开发系统使用的类型系统是JSON的超集,节点间传递和使用的值都遵从此类型系统。 值有以下类型:
- null:与JSON相同
- boolean:与JSON相同
- number:与JSON相同
- string:与JSON相同
- array:与JSON相同
- map:与JSON的object相同
- time:时间类型,不能从JSON中转换得到,转换为JSON时为字符串
变量
在业务逻辑开发页面上侧的操作栏中找到变量配置按钮,点击之后会出现变量配置页面,每个应用对应存在着不同的变量配置。添加完变量后可以在对应的服务中使用该变量。
添加变量 点击添加变量按钮出现侧边栏
配置项 | 说明 |
---|---|
变量名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过50个字符。 |
变量类型 | 设置变量的作用域 |
- 全局:此变量所有的服务都能可以访问到
- 局部:此变量只有当前的服务可以访问到 | | 数据类型 | 设置该变量的类型
- **Num (数值型):**输入数字,可以包含小数点
- **String (字符型):**输入字符串数据
- **Boolean (布尔型):**设置为true或者false
- **Time (时间型):**可选择具体时间点
- **JSON (结构体):**可设置JSON格式的数据 | | 默认值 | 可选:
- 设置该变量的默认值 | | 描述 | 可选:
- 对该变量的描述
- 字符长度最长为200 |
节点
节点是服务的功能单元。 在开发页面中,节点之间以相互串联的形式组成服务的运行流程。 HTTP触发、设备触发、定时触发为输入节点,每个服务都必须有一个输入节点,且只能有一个输出节点。 当输入节点是HTTP触发时,可以使用HTTP返回配置触发服务时HTTP请求的返回,否则HTTP返回不可用。
节点输出
有些节点会将其计算、运行的结果输出,供其他节点使用。
实例
每当服务的输入节点所设定的时间发生时,称服务被触发。每次服务触发时都会产生一个触发实例。 触发实例按照节点所配置的流程进行操作和运算,当执行到某个节点后找不到下一个节点时,实例运行结束。 如果触发实例产生时,当前服务下还有其他实例未结束,则服务的多个实例可以同时存在。 在实例运行时,如果产生错误(例如引用了不存在的输出、数值计算类型不匹配),实例会直接中止运行。 为了防止逻辑错误导致服务陷入死循环,当实例运行超过1000个节点后也会直接中止运行。
功能介绍
按照不同节点的功能,我们将节点分成了 6 个分组
输入节点
定时触发
在规定的时间内触发整个服务的运行。 定时触发节点用于设置时间,使服务在指定时间执行。常用于定时推送消息、定时执行任务、定时提醒、定时触发设备等场景。每个业务服务仅支持使用一个触发类型的节点。
节点配置
在业务逻辑编辑页面的** 输入列表 中,拖拽定时触发**节点到中间画布进行节点配置,如图所示
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
触发配置 | 触发配置是一个数组,可以设置多个触发规则,当任意一项触发规则满足时,即可触发流程,该选项目前最多支持十个条件 |
触发模式 |
- **单次触发 **(只触发一次):选择具体触发时间, 精确到分钟。
- 间隔固定分钟数触发:按分钟间隔进行规则出发,需要设置具体的时间间隔分钟数,范围 1~59分钟。
- 间隔固定小时数触发:按小时时间间隔进行规则触发。需设定具体的时间间隔小时数,范围:1-23小时。
- 每天固定时间触发一次:每天的固定时间进行触发。需设定具体的触发时间点,可精确到秒。
- **每周固定时间触发:**每周的固定时间进行触发。需选择周内触发日期(工作日、周末或周内某天)和具体的触发时间点,可精确到秒。
- **每月固定时间触发:**每月的固定时间进行触发。需选择月内具体日期(或选择最后一天)和具体触发时间点,可精确到秒。
- 选择生效时间。
- 选择结束时间。不设置该内容为永久生效。 | | 执行时间 | 需要流程触发的时间
- 单次触发模式时,该选项为具体的日期时间( yyyy-mm-dd hh:mm:ss )
- 以天为间隔触发时,该选项为具体的时间( hh:mm:ss )
- 以周为间隔触发时,该选项为具体的星期加**时间 **(星期几 hh:mm:ss)
- 以月为间隔触发时,该选项为具体的号数加**时间 **(几号 hh:mm:ss) | | 循环间隔 | 循环的间隔时间
- 以分钟为间隔触发时,该选项为**分钟数值 **(1~59)
- 以小时为间隔触发时,该选项为**小时数值 **(1~24) | | 生效时间 | 必填:当模式不为单次触发时显示,循环开启的时间 ( yyyy-mm-dd hh:mm:ss ) | | 结束时间 | 可选:当模式不为单次触发时显示,循环结束的时间 ( yyyy-mm-dd hh:mm:ss ) |
节点输出
节点的输出为触发时间。
触发模式的一些细节
- 间隔固定分钟数、间隔固定小时数触发时,生效时间不仅用于限定触发时间,还用于决定触发时间具体的时、分、秒。例如生效时间是某天0点,间隔2小时触发,则实际的触发时间是0点、2点、4点……对于其他触发模式,则需要手动指定具体的时、分、秒,生效时间只用于限定触发时间。
- 每月固定时间触发时,如果选择29、30、31日,但某月没有29、30、31日,则当月不触发
触发丢失
如果服务器停机维护,本应在停机期间定时触发的服务,在服务器重启后不会补齐
触发延迟
受服务器负载影响,实际触发时间可能会比预定时间略有延迟,延迟期间额外的触发计划不会补齐。例如预定某日00:00:00触发,实际触发时间可能是00:00:05,即使00:00:01还有一次预定触发,则00:00:05也触发一次。
设备触发
每当设备上报信息时,触发服务的运行。 设备触发节点是将设备上报的属性(读写型)、事件数据或状态变更作为服务的输入,触发服务后续的业务逻辑。设备触发节点支持通过虚拟设备上报属性或事件触发服务,帮助您自定义设备信息响应的服务流。
节点配置
在业务逻辑开发编辑页面的 输入列表 中,拖拽 **设备触发节点 **到中间画布并进行配置,如图所示。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
设备类型 | 选择需要的设备所属的类型 |
设备 | 可选: |
根据所属的设备类型选择设备,注意如果不选择设备的时候,当该类型分组下任意一个设备触发时都会触发此节点 | |
设备点位 | 可选: |
触发流程的点位 (注意:设备点位与状态之间至少需要选择一项) | |
状态 | 可选: |
触发流程的状态 (注意:设备点位与状态之间至少需要选择一项) |
节点输出
节点输出为map,字段如下:
字段名 | 类型 | 说明 |
---|---|---|
deviceTypeID | string | 触发设备的设备类型ID |
deviceID | string | 触发设备的设备ID |
time | time | 触发事件 |
state | string | 状态触发时,触发设备最新上报的状态 |
pointID | string | 点位触发时,触发设备最新上报的点位ID |
value | unknown | 点位触发时,触发设备最新上报的点位值 |
节点可以同时配置关心的点位和状态,但实例每次只能由点位或状态二者之一触发。点位触发时,节点输出包含pointID
和value
字段;状态触发时,节点输出包含state
字段。也即pointID
+value
字段和state
字段不会同时出现。如果同时配置了点位和状态,可以用“条件判断”或“路径选择”节点,比较相关字段是否为null
来判断当前实例的触发类型。
HTTP 触发
通过 HTTP 请求触发规则。 HTTP触发节点是创建API服务的开始节点,通过该节点可配置API的请求参数和SDK调用时的Action。每个API有且仅有一个HTTP触发节点,中间逻辑节点可根据业务需要选择其他功能节点,但必须以HTTP返回节点作为终止节点。
节点配置
在业务逻辑开发编辑页面的 输入列表 中,拖拽HTTP触发节点到中间画布并进行配置,如图所示。
配置项 | 说明 |
---|---|
节点名称 | 支持中文汉字、英文字母、数字和下划线(_)。长度不超过30个字符。 |
认证方式 |
- 是否需要做账号鉴权的选项
- 无:不使用鉴权
- **bearer:**使用 token 进行鉴权
- **basic:**使用用户名与密码的方式进行鉴权 | | token | 当认证方式为 bearer 时必须填写 | | 用户名 | 当认证方式为 basic 时必须填写 | | 密码 | 当认证方式为 basic 时必须填写 |
节点输出
节点的输出即是触发请求的请求体。其类型依赖于触发请求的Content-Type
请求头。 如果Content-Type
是text/plain
,则请求体整体被解析为字符串,再作为节点输出。 如果Content-Type
是application/json
,则请求体整体被解析为JSON,再作为节点输出。 如果请求头不含Content-Type
,请求体被忽略,节点输出为null。 除此之外的Content-Type
都会导致HTTP响应406 Not Acceptable。
请求认证
节点可以设置Basic或Bearer认证:
- Basic:触发请求应有请求头
Authorization: Basic {token}
,其中{token}
应为节点指定的用户名、密码,中间以冒号拼接后,再用base64编码的字符串。例如用户名为hello
,密码为world
,则{token}
应替换为hello:world
的base64编码,即aGVsbG86d29ybGQ=
- Bearer:触发请求应有请求头
Authorization: Bearer {token}
,其中{token}
应为节点指定的token
未设置认证时,请求不需要Authorization
请求头,如有则被忽略。 认证未通过会导致HTTP响应401 Unauthorized。
触发请求
向平台发送如下HTTP请求即可触发:
POST /logic/api/v1/service/{service_id}/trigger
其中{service_id}
替换为服务的ID。
触发成功时,触发请求的响应码一般由HTTP响应节点指定。以下响应码有可能不是由HTTP返回节点指定的:
响应码 | 原因 |
---|---|
201 Created | ①实例已完成,但没有终止于HTTP返回节点 |
②实例在一定的时间内没有终止 | |
③实例由于运行节点数超过限制而被中止 | |
400 Bad Request | 无法按照指定的Content-Type解析请求体 |
401 Unauthorized | 节点开启了认证,但Authorization不存在或与没有通过认证 |
404 Not Found | 指定的服务不存在,或服务没有HTTP触发节点 |
406 Not Acceptable | Content-Type不是text/plain或application/json |
413 Request Entity Too Large | 请求体过大 |
500 Internal Server Error | 服务器内部错误 |
502 Bad Gateway | 服务器内部错误 |
503 Service Unavailable | 服务器内部错误 |
504 Gateway Timeout | 服务器内部错误 |
MQTT订阅
MQTT 订阅到的消息将以 JSON 格式解析作为节点输出。 MQTT 订阅节点所使用的连接与 MQTT 发布节点相互独立。 MQTT 订阅节点使用30秒心跳,短线重连。如果初始连接失败超过10次(不包括断线重连),则当前服务不再重试连接,必须重新发布服务才会重新开始连接。
输出节点
HTTP返回
配置 HTTP 请求返回的数据,返回码等内容。 HTTP返回节点可配置为业务服务的结束节点。使用HTTP请求节点配置HTTP接口时,可以使用HTTP返回节点作为结束节点,来配置HTTP请求的返回值。 HTTP返回节点必须搭配HTTP请求节点使用,如果服务的输入节点不是HTTP请求,则不能使用HTTP返回节点。
节点配置
在业务逻辑编辑页面的节点中,选择对应功能节点配置业务流,HTTP返回节点配置页面如下图所示。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
返回码 | HTTP返回的状态码,值区间为 [100, 600] |
输出 | 设置API返回参数为: |
- 不同数据类型的固定值。
- 来自节点的值。
- 上一个节点输出的payload的子属性。
- 某个前置节点的输出对象payload或其子属性。
- API的请求参数。
- 变量值 (已添加的变量):添加变量的详细内容请参见添加变量。 |
功能节点
条件判断
根据选择的数据进行 if-else 判断以分配后续路径。 条件判断节点根据设定的条件对输入值进行判断,再根据判断结果执行不同的路径。条件判断结果产生两个路径:满足条件的路径和不满足条件的路径。
节点配置
在业务逻辑编辑页面的节点列表中,选择** 功能列表** 下的 条件判断 节点,条件判断节点配置页面如下图所示。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
条件配置 | 单击 **添加条件 **添加判断条件,您可以为当前节点添加多个条件。 |
- 用于比较的数据源,可设置为:
- 常量:静态数据。可选数据类型:
- 数值型:输入数字,可以包含小数点。
- 布尔值:设置为true或者false。
- 字符串:输入字符串数据。
- 时间型:可选择具体时间点。
- JSON:可以用名称或值对的方式来表达复杂的数据格式,需要采用JSON格式书写。对象可以包含多个名称或值对。例如{ "firstName":"John" , "lastName":"Doe" }
- 来自节点:设置节点的值。
- 上一节点(payload):需结合上一个节点的输出数据格式。可以手动输入上一个节点的变量名称,则调用该变量对应的值;如果不填写变量,则返回上个节点的默认值或全部返回值。
- 本节点之前的任一节点的某个参数。
- 变量:设置为已添加的变量。有关变量配置的详细内容,请参见变量配置。
- 常量:静态数据。可选数据类型:
- 比较方式:大于、大于等于、小于、小于等于、等于、不等于、为空、非空。 | | | 支持调整条件的前后顺序,配置条件之间的满足关系。
- AND:条件都满足时,判断为true;否则,判断为false。
- OR:满足任意一个条件时,判断为true;当所有条件均不满足时,判断为false。
有多个条件时,从上至下依次进行布尔运算,得出最终的运算结果为true则执行满足条件的分支,为false则执行不满足条件的分支。 例如:条件1为true,条件2为false,条件3为true,依次设置条件关系为AND、OR,则true&& false || true的运算结果为true,即执行满足条件的分支。 |
路径选择
路径选择节点可以根据设定的规则,对数据源进行判定,从而执行不同路径逻辑。当输入值满足路径1的条件时,执行路径1;不满足时,继续判断路径2的条件;当所有条件都不满足时执行路径else的条件。
使用场景
如果需要对前一节点的内容输入值做出判断,并根据判断结果执行不同的逻辑,则可以使用路径选择节点。典型使用场景如下所示。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
输入 | 路径选择的条件均需与输入的数据源进行对比。 |
- 常量:静态数据。可选数据类型: - 数值型:输入数字,可以包含小数点。 - 布尔值:设置为true或者false。 - 字符串:输入字符串数据。 - 时间型:可选择具体时间点。 - JSON:可以用名称或值对的方式来表达复杂的数据格式,需要采用JSON格式书写。对象可以包含多个名称或值对。例如{ "firstName":"John" , "lastName":"Doe" }
- 来自节点:设置节点的值。
- 上一节点(payload):需结合上一个节点的输出数据格式。可以手动输入上一个节点的变量名称,则调用该变量对应的值;如果不填写变量,则返回上个节点的默认值或全部返回值。
- 本节点之前的任一节点的某个参数。
- 变量:设置为已添加的变量。有关变量配置的详细内容,请参见变量配置。 | | 路径配置 |
- 来自节点:设置节点的值。
- 节点上默认没有任何路径,当创建了一个路径之后自动产生 else 路径,路径内选项存在比较选项
- 比较条件:
>
、<
、>=
、<=
、==
、!=
、null
(为空)、!null
(非空)、true
、false
- 比较数据源:
- 当 **比较条件 **为 null、!null、true、false 时不需要选择被比较的数据源
- 当 比较条件 为其他情况需要选择被比较的数据源,格式与输入节点相同
- 其他路径 (else):当所有条件不满足时执行此条路径 |
条件判断
条件判断节点根据设定的条件对输入值进行判断,再根据判断结果执行不同的路径。条件判断结果产生两个路径:满足条件的路径和不满足条件的路径。
节点配置
在业务逻辑编辑页面的节点列表中,选择** 功能列表** 下的 条件判断 节点,条件判断节点配置页面如下图所示。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
条件配置 | 单击 **添加条件 **添加判断条件,您可以为当前节点添加多个条件。 |
- 用于比较的数据源,可设置为:
- 常量:静态数据。可选数据类型:
- 数值型:输入数字,可以包含小数点。
- 布尔值:设置为true或者false。
- 字符串:输入字符串数据。
- 时间型:可选择具体时间点。
- JSON:可以用名称或值对的方式来表达复杂的数据格式,需要采用JSON格式书写。对象可以包含多个名称或值对。例如{ "firstName":"John" , "lastName":"Doe" }
- 来自节点:设置节点的值。
- 上一节点(payload):需结合上一个节点的输出数据格式。可以手动输入上一个节点的变量名称,则调用该变量对应的值;如果不填写变量,则返回上个节点的默认值或全部返回值。
- 本节点之前的任一节点的某个参数。
- 变量:设置为已添加的变量。有关变量配置的详细内容,请参见变量配置。
- 常量:静态数据。可选数据类型:
- 比较方式:大于、大于等于、小于、小于等于、等于、不等于、为空、非空。 | | | 支持调整条件的前后顺序,配置条件之间的满足关系。
- AND:条件都满足时,判断为true;否则,判断为false。
- OR:满足任意一个条件时,判断为true;当所有条件均不满足时,判断为false。
有多个条件时,从上至下依次进行布尔运算,得出最终的运算结果为true则执行满足条件的分支,为false则执行不满足条件的分支。 例如:条件1为true,条件2为false,条件3为true,依次设置条件关系为AND、OR,则true&& false || true的运算结果为true,即执行满足条件的分支。 |
JS脚本
JS 脚本支持 ECMAScript 5.1 语法规则,不支持引入第三方库。
节点输出
如果脚本执行成功,脚本中最后一个表达式的值将成为节点的输出。 JavaScript类型和输出类型的对应关系:
JavaScript类型(typeof) | 输出类型 |
---|---|
undefined | null |
symbol | string |
string | string |
number | number |
bigint | number |
function | string,值为"[Function $name]" |
boolean | boolean |
object |
- 如果值为null,输出null
- 如果是特定实例,按对应类型输出
- 否则,输出map,键为其getOwnPropertyNames |
特定实例如下:
object实例类型(instanceof) | 输出类型 |
---|---|
Number | number |
Boolean | boolean |
String | string |
Date | time |
Array | array |
Map | map |
Set | map,值皆为null |
Error | string,值为toString() 返回的字符串 |
全局变量
payload
typescript
const payload: unknown
全局变量payload
为上一节点的输出。上一节点无输出时,payload
为null
。
query
typescript
const query: unknown
全局变量query
为输入节点的输出。
node
typescript
const node: { [nodeID: string]: unknown }
全局变量node
为一个object,其键为已运行节点的ID,值为对应节点的输出。对应节点无输出时,值为null
。节点未运行时,node
上不存在节点的键。
javascript
// 节点 node_25f6e4 的输出为 { "rows": [{ "id": 1 }] }
console.log(node.node_25f6e4.rows[0].id); // 引用节点 node_25f6e4 的输出并打印到日志
env
typescript
const env: { [variable: string]: unknown }
全局变量env
为一个Proxy,以变量名(包括局部变量和全局变量)为键可以查询变量的值,变量的值可以在代码中修改。
javascript
console.log(env.variable1); // 查询变量 variable1 的值并打印到日志
env.variable1 = 1; // 将变量 variable1 的值设为 1
kv
typescript
const kv: { [key: string]: unknown }
全局变量kv
为一个Proxy,用于访问键值储存。用法和env
类似,并可以删除特定键。
javascript
console.log(kv.running); // 查询键 running 的值并打印到日志
kv.running = 1; // 将键 running 的值设为 1
delete kv.running; // 删除键 running
console
typescript
const console: {
log: (...args: any[]) => void;
debug: (...args: any[]) => void;
info: (...args: any[]) => void;
warn: (...args: any[]) => void;
error: (...args: any[]) => void;
}
全局变量console
为一个object,其方法debug
、info
(等同于log
)、warn
、error
可以用于向日志打印消息。上述方法会将每个参数转为字符串,以空格为间隔拼接。 注意:这个对象与 Web API 或 node.js 不兼容。 device
typescript
const device: { [deviceID: string]: Device }
type Device = {
point: DevicePointAPI;
prop: DevicePropertyAPI;
}
interface DevicePointAPI {
get(this: DevicePointAPI, pointID: string): unknown;
get(this: DevicePointAPI, ...pointIDs: string[]): { [pointID: string]: unknown };
set(this: DevicePointAPI, pointID: string, value: any): void;
set(this: DevicePointAPI, Map<string, any> | { [pointID: string]: any }): void;
}
interface DevicePropertyAPI {
get(this: DevicePropertyAPI, propertyID: string): unknown;
get(this: DevicePropertyAPI, ...propertyIDs: string[]): { [propertyID: string]: unknown };
}
全局变量device
为一个Proxy,以设备ID为键可以获取到对应设备的Device
对象。获取Device
对象时,不会检查设备是否存在。 Device
对象为绑定到某台具体设备的一个object。其属性point
为同一设备的DevicePointAPI
对象,prop
为同一设备的DevicePropertyAPI
对象。 DevicePointAPI
对象为绑定到某台具体设备的一个object,用于访问其点位。方法get
传入点位ID用于查询一个点位值,传入多个点位ID时返回值为object形式。方法set
传入点位ID和点位值时用于下发点位值,传入一个object时将以其键为点位ID、值为点位值一次性下发多个点位值,也可以传入一个Map
。 DevicePropertyAPI
对象和DevicePointAPI
对象类似,用于访问设备的属性。目前只能查询而不能修改。
javascript
// 查询设备 SD1 点位 V1 的值
console.log(device.SD1.point.get('V1')); // => 42.0
// 查询设备 SD1 点位 V1、V2 的值
console.log(device.SD1.point.get('V1', 'V2')); // => { "V1": 42.0, "V2": 11.5 }
// 下发设备 SD1 点位 V1 的值
device.SD1.point.set('V1', 60);
// 下发设备 SD1 点位 V1、V2 的值
device.SD1.point.set({ V1: 60, V2: 45 });
// 查询设备 SD1 属性 label 的值
console.log(device.SD1.prop.get('label')); // => "golden"
// 查询设备 SD1 属性 label、state 的值
console.log(device.SD1.prop.get('label', 'state')); // => { "label": "golden", "state": "ready" }
Python脚本
Starlark 语言是Python语言的一门方言。本系统中使用的 Python 脚本基于 Starlark 实现。
脚本结构
如果脚本中定义了一个名为main
的函数,则执行脚本后会调用一次此函数(不带任何参数),其返回值转换为为节点的输出。
python
def main():
# ......
如果脚本中没有定义main
的函数,则执行脚本后节点没有输出。
类型关系
Starlark类型(type) | 输出类型 |
---|---|
NoneType | null |
string | string |
int、float | number |
function | string,值为"[function $name]" |
builtin_function_or_method | string,值为"[function $name]" |
dict | map,仅包含string类型键的值 |
set | map,值皆为null |
list、tuple | array |
module | string,值为"[module $name]" |
输入类型 | Starlark类型(type) |
---|---|
null | NoneType |
string | string |
number | 如果包含小数部分则为float,否则为int |
map | dict |
array | list |
time | int,值为对应的毫秒级UNIX时间戳 |
全局变量
payload 全局变量payload
为上一节点的输出。上一节点无输出时,payload
为None
。 query 全局变量query
为输入节点的输出。 node 全局变量node
为一个dict,其键为已运行节点的ID,值为对应节点的输出。对应节点无输出时,值为None
。节点未运行时,node
上不存在节点的键。
python
# 节点 node_25f6e4 的输出为 { "rows": [{ "id": 1 }] }
log.debug(node["node_25f6e4"]["rows"][0]["id"]); # 引用节点 node_25f6e4 的输出并打印到日志
内置模块和函数
env
env.get
查询变量(包括局部变量和全局变量)env.set
修改变量
python
log.debug(env.get("variable1")) # 查询变量 variable1 的值并打印到日志
env.set("variable1, 1) # 将变量 variable1 的值设为 1
kv
kv.get
查询键值储存kv.set
修改键值储存kv.delete
删除键
python
log.debug(kv.get("running")) # 查询键 running 的值并打印到日志
kv.set("running", 1) # 将键 running 的值设为 1
kv.delete("running") # 删除键 running
log
log.debug
log.info
log.warn
log.error
打印日志。 device
device.get
查询点位值。参数为设备标识、点位标识(可以多个)。查询多个点位时返回dict。device.set
下发点位值。下发单个点时,参数为设备标识、点位标识、值;下发多个点时,参数为设备标识、点位标识和值的dict。device.prop
查询属性值,与查询点位值类似
python
# 查询设备 SD1 点位 V1 的值
log.debug(device.get('SD1', 'V1')) # => 42.0
# 查询设备 SD1 点位 V1、V2 的值
log.debug(device.get('SD1', 'V1', 'V2')) # => { "V1": 42.0, "V2": 11.5 }
# 下发设备 SD1 点位 V1 的值
device.set('SD1', 'V1', 60)
# 下发设备 SD1 点位 V1、V2 的值
device.set('SD1', { "V1": 60, "V2": 45 })
# 查询设备 SD1 属性 label 的值
log.debug(device.prop('SD1', 'label')) # => "golden"
# 查询设备 SD1 属性 label、state 的值
log.debug(device.prop('SD1', 'label', 'state')) # => { "label": "golden", "state": "ready" }
数值计算 对来自设备,API等的数据进行数值计算并输出结果 设备操作 对设备进行操作
数值计算
使用数值计算节点,您无需写代码就能实现简单的逻辑运算,例如多个设备属性值相运算、多个设备属性值之间取最大值、最小值、平均值等简单的逻辑运算操作。
节点配置
在业务逻辑编辑页面的** 功能列表** 下,拖拽数值计算节点到中间画布中,进行配置。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
输入设置 | 可设置为常量(静态数值)、来自节点、变量(已添加的变量)。 |
**注意:**数值计算节点的数值类型只能是数值型数据,如果选择的数据类型不为数值类型可能会引发错误。 | |
运算方法 | 选择数据源输入值与参数值的计算方法。目前支持的计算能力包含:相加、相减、相乘、相除、最大值、最小值、平均值。 |
添加参数 | 设置与数据源输入值进行计算的参数值。可以添加多个参数。 |
设备操作
设备操作节点,主要用于向设备进行查询或下发操作。
节点配置
在业务逻辑编辑页面的** 功能列表** 下,拖拽设备操作节点到中间画布中,进行配置。
配置项 | 描述 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
设备类型 | 可选: |
- 所需要操作的设备的所属类型 | | 设备 | 所需要操作的设备 | | 操作类型 |
- 属性查询:对设备上存在的属性结果进行数据查询
- 点位查询:针对设备上存在的点位进行数据查询
- 点位下发:针对设备进行数据下发 | | 多选 | 当操作类型为属性查询或点位查询时出现,用于控制 设备属性 与 **设备点位 **是否为多选 | | 设备属性 |
- 操作类型为属性查询时需要
- 需要查询的设备属性,当多选为真时该选项为多选 | | 设备点位 |
- 操作类型为点位查询时需要
- 需要查询的设备点位,当多选为真时该选项为多选 | | 下发数据 |
- 操作类型为点位下发时需要
- 需要设置下发点位
- 需要设置想要下发数据,详情见下方 **数据选择 **字段 | | 数据选择 |
- 用于设置需要下发的数据源,可设置为:
- 常量:静态数据。可选数据类型:
- 数值型:输入数字,可以包含小数点。
- 布尔值:设置为true或者false。
- 字符串:输入字符串数据。
- 时间型:可选择具体时间点。
- JSON:可以用名称或值对的方式来表达复杂的数据格式,需要采用JSON格式书写。对象可以包含多个名称或值对。例如{ "firstName":"John" , "lastName":"Doe" }
- 来自节点:设置节点的值。
- 上一节点(payload):需结合上一个节点的输出数据格式。可以手动输入上一个节点的变量名称,则调用该变量对应的值;如果不填写变量,则返回上个节点的默认值或全部返回值。
- 本节点之前的任一节点的某个参数。
- 变量:设置为已添加的变量。有关变量配置的详细内容,请参见变量配置。 |
- 常量:静态数据。可选数据类型:
节点输出
操作类型为点位查询时,节点包含输出。 单点位查询时,输出为点位值。 多点位查询时,输出为以点位ID为键、点位值为值的map。
操作类型为属性查询时,节点包含输出。 单属性查询时,输出为属性值。 多属性查询时,输出为以属性ID为键、属性值为值的map。
操作类型为点位下发时,节点无输出。
消息节点
短信
MQTT发布
连接池
如果同一业务逻辑下两个MQTT发布节点的数据库地址、端口、用户名、心跳都相同,则它们共用同一条连接。 自连接成功后,只要此连接被使用(包括被建立连接的实例使用,也包括被后续其他实例使用),则连接被保留,即使实例结束也继续保留。如果持续5分钟没有使用,则连接被主动关闭。
消息机器人(暂未提供)
调用 Webhook 向钉钉机器人推送消息。 调用 Webhook 向飞书机器人推送消息。 调用 Webhook 向企业微信机器人推送消息。
API调用节点
自定义API
支持调用外部的API,返回 body 并完整传递给下一节点。
节点配置
在业务逻辑编辑页面的 API调用 > **自定义API **中,选择对应功能节点配置业务流,该节点配置如下。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
请求方式 | 自定义API的请求方法支持GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH、TRACE。 |
API地址 | 需要调用的API的地址。此项为**“模板字符串”**。 |
请求头 | 以JSON对象格式书写的请求头,其键位请求头名称,值位请求头值的**“模板字符串”** |
Body类型 | (未发布) |
- form-data
- x-www-form-urlencoded
- json
- raw | | 参数编写 | 请求体,此项为**“模板字符串”** | | JSONPath | (未发布)如果不为空,响应体将解析为JSON,按照此路径指定的值成为节点输出 |
模板JSON
模板JSON是“模板字符串”的一种特殊形式。 JSON中object的键被视为模板字符串进行替换。 JSON中object的值,如果是字符串,则被视为模板字符串进行替换。特别地,当此模板字符串中只有一个path时,path对应的值将按对应类型替换到JSON。 例如payload
是{ x: 3 }
:
{ "x": "${payload.x}" }
替换后为{ "x": 3 }
,object 值的部分被替换,且替换为数值。{ "${payload.x}": "${payload.x}" }
变量替换后为{ "3": 3 }
,object 键和值的部分被替换,但只有值的部分被替换为数值,键的部分仍为字符串。{ "x": "value = ${payload.x}" }
变量替换后为{ "x": "value = 3" }
,object 值的部分被替换,但由于path的两侧还有其他字符,替换后仍为字符串形式。{ "x": "${payload.x}${payload.x}" }
变量替换后为{ "x": "33" }
,object 值的部分被替换,但由于存在多个path,因此替换后仍为字符串形式。
节点输出
节点没有输出。 (未发布)如果JSONPath为空,节点没有输出;如果JSONPath不为空,响应体解析为JSON后,按照JSONPath指定的值为节点输出。
数据节点
变量设置
变量设置节点可修改已添加变量值。使用该节点,设置变量值作为当前服务的中间逻辑功能,并将设置后的值作为节点的输出。 节点配置 在业务逻辑编辑页面的 数据 下,拖拽变量设置节点到中间画布中。
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
变量设置 | 添加待修改的变量。 |
注意: 修改变量只能在同数据类型之间进行修改
数据库操作
使用数据库操作节点对数据库进行 增删改查 等操作。
节点配置
在业务逻辑编辑页面的节点中,选择 **数据库操作 **节点
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文、英文字母、数字和下划线(_),长度不超过30个字符 |
数据库类型 | 目前只支持 MySQL 与 PostgreSQL 两种类型的数据库 |
用户名 | 数据库的用户名 |
密码 | 数据库的密码(可选) |
连接地址 | 填入实例的数据库外网地址 |
数据库名 | 需要连接的数据库名称 |
端口号 | 需要连接的数据库端口 |
- MySQL 默认为 3306
- PostgreSQL 默认为 5432 | | 操作类型 | 需要对数据库进行的操作
- 查询:查询数据库中的数据
- 插入:在数据库中插入数据
- 更新:更新数据库中的数据
- 删除:删除数据库中的数据 | | 参数 | 根据不同的操作类型,需要我们使用不同的模板写法 |
节点输出
此节点包含输出。输出形式根据操作的不同而不同,详见后文。
连接池
如果同一业务逻辑下两个数据库节点的数据库类型、地址、端口、数据库名、用户名、schema都相同,则它们共用同一条数据库连接。 自数据连接成功后,只要此数据库连接被使用(包括被建立连接的实例使用,也包括被后续其他实例使用),则数据库连接被保留,即使实例结束也继续保留。如果持续5分钟没有使用,则数据库连接被主动关闭。
基础:SqlValue
typescript
type SqlValue =
| number
| boolean
| null
| Template
SqlValue
表示参与运算的值。有以下几种形式:
number
类型的常量boolean
类型的常量null
- 一个**“模板字符串”**
对于模板字符串,其根据模板本身的格式,有以下几种情况:
- 字符串常量:如果模板字符串中不包含变量替换,则被视为字符串常量参与运算。如
"hello"
- 模板字符串:如果模板字符串中包含不止一个变量替换,或者除了变量替换之外还有其他字符(包括空格),则在进行变量替换后,以字符串形式参与运算。如
"x is ${x}"
- 模板值:如果模板字符串中有且仅有一个替换,且变量替换的两边没有其他字符(包括空格),则在进行变量替换后,按照替换结果的类型参与运算。如
"${ payload.x }"
对于最后一种模板值的情况,如果替换结果为number
、boolean
、null
、string
、time
类型的值,则使用此值参与运算;如果替换结果为array
、map
类型的值,则会再次转换为string
类型再参与运算。
基础:Where
typescript
type Where =
| SqlValue
| WhereSimpleForm
| WhereComplexForm
type WhereSimpleForm = { [column: string]: WhereSimpleOperator }
type WhereSimpleOperator =
| SqlValue
| { "$eq": SqlValue }
| { "$neq": SqlValue }
| { "$gt": SqlValue }
| { "$gte": SqlValue }
| { "$lt": SqlValue }
| { "$lte": SqlValue }
| { "$like": SqlValue }
type WhereComplexForm =
| Where[]
| { "$expr": SqlValue }
| { "$column": string }
| { "$and": Where[] }
| { "$or": Where[] }
| { "$not": Where }
| { "$eq": [Where, Where] }
| { "$neq": [Where, Where] }
| { "$gt": [Where, Where] }
| { "$gte": [Where, Where] }
| { "$lt": [Where, Where] }
| { "$lte": [Where, Where] }
| { "$like": [Where, Where] }
Where
用于表示Select
、Update
、Delete
所使用的限定条件。
Where
是可选的,如果省略,则生成的语句中没有WHERE
:
json
{
"table": "users",
"row": ["id"]
}
sql
SELECT id FROM users;
Where 设为一个 SqlValueWhere
可以直接设为一个SqlValue
。 如果这个SqlValue
是null
,则Where
本身被忽略。否则,Where
的值将会被数据库隐式转换为布尔值使用,包括模板值替换之后得到的null
(被数据库识别为false
)。 需要直接使用SqlValue
作为Where
场景比较少见。一种使用场景是,如果数据库开启了严格检查,不带WHERE
的UPDATE
和DELETE
语句会报错:
json
{
"table": "users",
"where": true
}
sql
DELETE FROM users WHERE true;
Where 的简单键值形式Where
的简单键值形式是一个JSON对象,其键为列名,如果值为SqlValue
则表示相等:
json
{
"table": "users",
"row": ["id"],
"where": {
"age": "${ payload.age }"
}
}
sql
-- payload : {
-- "age": 35,
-- }
SELECT id FROM users WHERE age = 35;
多个键会按照字母表顺序排列后用AND
链接。如果有逻辑短路的需求,需要用复杂形式严格指定顺序。
json
{
"table": "users",
"row": ["id"],
"where": {
"is_manager": true,
"age": "${ payload.age }"
}
}
sql
-- payload : {
-- "age": 35,
-- }
SELECT id FROM users WHERE age = 35 AND is_manager = true;
简单键值形式的值可以是一个键为"$eq"
、"$neq"
、"$gt"
、"$gte"
、"$lt"
、"$lte"
、"$like"
之一特殊值,值为SqlValue的JSON对象,分别表示等于、不等于、大于、大于等于、小于、小于等于、LIKE
:
json
{
"table": "users",
"row": ["id"],
"where": {
"age": {"$gte": 60}
}
}
sql
SELECT id FROM users WHERE age >= 60;
这种值必须只有一对键值,否则不保证哪一种特殊键被识别。
json
{
"table": "users",
"row": ["id"],
"where": {
"age": {"$gte": 60, "$lt": 80}
}
}
sql
SELECT id FROM users WHERE age >= 60;
-- 也有可能是:
-- SELECT id FROM users WHERE age < 80;
简单键值形式的列名不能以美元符号开头,如果有此需求,需要用复杂形式表示。 如果简单键值形式的键值数量为0,则WHERE
本身被省略。
Where 的复杂形式 Where的复杂形式是一个JSON对象,其键为美元符号开头的特殊键。 复杂形式只能有一对键值,否则不保证哪一种特殊键被识别。 特殊键如下:
"$expr"
:值应为一个SqlValue
,表示一个常量值"$column"
:值应为一个字符串常量,表示列名"$and"
、"$or"
:值应为一个嵌套的Where
的列表(包括SqlValue
、简单键值形式、复杂形式),列表长度不限,表示AND
、OR
。如果列表长度为0,则不生成SQL表达式。"$not"
:值应为一个嵌套的Where
,表示NOT
"$eq"
、"$neq"
、"$gt"
、"$gte"
、"$lt"
、"$lte"
、"$like"
:值应为一个嵌套的Where
的列表(包括SqlValue
、简单键值形式、复杂形式),且列表长度必须为2,分别表示等于、不等于、大于、大于等于、小于、小于等于、LIKE
json
{
"table": "users",
"row": ["id"],
"where": {
"$or": [
{"is_manager": true},
{"age": {"$gte": 60}},
{"$neq": [{"$column": "locked"}, false]}
]
}
}
sql
SELECT id FROM users
WHERE
is_manager = true OR
age >= 60 OR
users."locked" <> false;
这个例子中可以看到,WHERE
为使用了特殊键"$or"
的复杂形式,前两个嵌套的Where
是简单形式,并且没有按字母表顺序重新排序,最后第三个嵌套的Where
是复杂形式。
特别地,复杂形式如果是"$and"
的话,可以简写为一个列表。 运算符优先级 使用复杂形式的时要注意,需要用嵌套关系来表达运算符优先级关系。
sql
SELECT id FROM users
WHERE
is_manager = true OR
age >= 60 AND
locked = true;
在这个例子中,因为AND
的优先级比OR
高,所以使用括号显式表达的语句是这样的:
sql
SELECT id FROM users
WHERE
is_manager = true OR
(age >= 60 AND locked = true);
代码如下:
json
{
"table": "users",
"row": ["id"],
"where": {
"$or": [
{"is_manager": true},
[
{"age": {"$gte": 60}},
{"locked": true}
]
]
}
}
如果需要先结合OR
两侧的条件,应当显式进行结合:
sql
SELECT id FROM users
WHERE
(is_manager = true OR age >= 60) AND
locked = true;
json
{
"table": "users",
"row": ["id"],
"where": [
{
"$or": [
{"is_manager": true},
{"age": {"$gte": 60}}
]
},
{"locked": true}
]
}
这个例子同时展示了将"$and"
简写为列表。
Select
typescript
type Select = {
'table': string;
'row': '*' | SelectColumn[];
'where'?: Where;
'limit'?: number;
'offset'?: number;
'order'?: OrderBy[];
'group'?: string[];
}
type SelectColumn =
| Selectable
| { [alias: string]: Selectable }
type Selectable =
| string
| { "$column": string }
| { "$count": string | null }
| { "$max": string }
| { "$min": string }
type OrderBy =
| string
| { "$asc": string }
| { "$desc": string }
json
{
"table": "users",
"row": [{"user_id": "id"}, "name"],
"where": {
"name": "admin",
"locked": false
}
}
sql
SELECT id AS user_id, name FROM users WHERE name = 'admin' AND locked = false;
Select的输出为列表,列表元素为查询结果的行。
json
[
{"user_id": "0001", "name": "john"},
{"user_id": "0002", "name": "ruby"}
]
**SELECT * **
将row设为字符串"*"
可以实现SELECT *
。
json
{
"table": "users",
"row": "*",
"where": {
"name": "admin",
"locked": false
}
}
sql
SELECT * FROM users WHERE name = 'admin' AND locked = false;
简单聚合 Select支持几种简单聚合,为以下几种特殊键的JSON对象,值为列名:
"$count"
"$max"
"$min"
特别地,{ "$count": null }
表示COUNT(*)
。 注意聚合不能直接作为row,而需要作为其元素。
json
{
"table": "users",
"row": [{ "$count": null }],
"where": {
"name": "admin",
"locked": false
}
}
sql
SELECT COUNT(*) FROM users WHERE name = 'admin' AND locked = false;
聚合语义校验 使用聚合时,非聚合的列必须在group
中使用。
json
{
"table": "users",
"row": [{ "$count": null }, "age"]
}
此例中使用了聚合,但非聚合列age
没有在group
中使用。 修正方法是将非聚合列age
添加到group
中。
json
{
"table": "users",
"row": [{ "$count": null }, "age"],
"group": ["age"]
}
另一种修正方法是将非聚合列age
改为聚合列。
json
{
"table": "users",
"row": [{ "$count": null }, { "$max": "age" }]
}
另外,如果group
不为空,即使row
为"*"
或者row
中没有聚合列,当前操作也视为使用了聚合。
json
{
"table": "users",
"row": ["age"],
"group": ["is_manager"]
}
聚合语义在发布时进行校验,并非在运行时由数据库进行校验。
排序 order为排序列的列表,可以用特殊键"$asc"
、"$desc"
的JSON对象显式表示升序或降序:
json
{
"table": "users",
"row": "*",
"order": ["age", { "$desc": "name" }, { "$asc": "id" }]
}
sql
SELECT * FROM users ORDER BY age, name DESC, id ASC;
Insert
typescript
type Insert = {
'table': string;
'rows': InsertRow[];
}
type InsertRow = { [column: string]: SqlValue }
json
{
"table": "users",
"rows": [
{"name": "my name is ${ payload.john }", "age": 35},
{"name": "my name is ${ payload.ruby }", "age": 40}
]
}
sql
-- payload : {
-- "john": "John Ted",
-- "ruby": "Ruby Gem",
-- }
INSERT INTO users (name, age) VALUES (
('my name is John Ted', 35),
('my name is Ruby Gem', 40)
);
生成的语句中,列名取所有输入行中列名的并集,如果某输入行缺少对应列,以NULL
填充:
json
{
"table": "users",
"rows": [
{"id": 1},
{"id": 2, "name": "Wink"}
]
}
sql
INSERT INTO users (id, name) VALUES (
(1, NULL),
(2, 'Wink')
);
Insert的输出为map,值rowsAffected
为插入的行数。
json
{
"rowsAffected": 2
}
Update
typescript
type Update = {
'table': string;
'row': InsertRow;
'where'?: Where;
}
json
{
"table": "users",
"row": {"locked": true},
"where": {
"age": {"$gte": 60}
}
}
sql
UPDATE users SET locked = true WHERE age >= 60;
Update的输出为map,值rowsAffected
为影响的行数。
json
{
"rowsAffected": 1
}
注意:根据数据库的不同此处返回结果可能会不同。有的数据库将WHERE
匹配到的行数作为影响的行数,而有的数据库则会对比更新的值和原值,值发生改变的行才算作影响的行。
Delete
typescript
type Delete = {
'table': string;
'where'?: Where;
}
json
{
"table": "users",
"where": {
"age": {"$gte": "${vpt}"}
}
}
sql
DELETE FROM users WHERE age >= 60;
Delete的输出为map,值rowsAffected
为影响的行数。
json
{
"rowsAffected": 1
}
键值对操作
键值对操作节点封装了KV存储服务API。使用该节点以键值对形式进行数据的写入、获取或删除操作。
节点配置
在业务逻辑编辑页面的节点中,选择 **键值对操作 **节点
配置项 | 说明 |
---|---|
节点名称 | 设置节点名称。支持中文汉字、英文字母、数字和下划线(_),长度不超过30个字符。 |
操作类型 | 可选: |
- KV存储获取
- KV存储写入
- KV存储删除 | | 键(key) | 设置要操作的键值对的键。可设置为固定值、上一节点(payload)、本节点之前的任一节点的某个参数或变量。 | | 值(value) | 当操作类型选择为KV存储写入时出现的参数。设置要写入的值。可设置为固定值、上一节点(payload)、本节点之前的任一节点的某个参数或变量。 |
节点输出
操作为读取时,节点输出为读取的值。 操作为写入、删除时,节点无输出。
键值储存和全局变量的区别
局部变量的作用域为当前实例,每次触发时初始化,实例之间互不影响。 全局变量的作用域为整个项目,进行持久化。没有实例修改其值时,项目下所有实例读取到的都是变量的默认值;当某实例修改了全局变量的值时,同一项目下所有其他实例都可以读取到设置的值。 键值储存的作用域为当前服务,进行持久化。没有实例修改其值时,服务下所有实例读取到的都是变量的默认值;当某实例修改了全局变量的值时,同一服务下所有其他实例都可以读取到设置的值。但同一项目下其他服务的实例无法读取当前服务的键值储存,换言之其他服务下可以使用相同键,服务之间互不影响。
模板字符串
在模板中,对于大括号${}
括起的 path,按此 path 访问变量得到的值将以字符串形式替换到模板中。大括号和 path 之间可以有空格。 如果模板中包含美元符号本身,需要写两次以转义$$
。 如果美元符号的下一个符号不是$
或{
,也表示美元符号本身。 在渲染到字符串时,字符串类型的值不会带有引号,如要保留引号,可以在 path 后加 | raw
。 在模板中,payload
、query
、node
三个变量被用于引用上一节点的输出、输入节点的数据和任意节点的输出,因此尽量不要用这三个名字定义变量。payload
和query
可以整体引用,但node
不能整体引用。payload
、query
、node
可以后接**“访问路径”,除此之外只能引用到变量本身,不能接“访问路径”**。 例如:变量x
为"false"
,payload
是{"args": ["ok"]}
,模板:
arg0 = ${ payload.args[0] }
quoted arg0 = ${ payload.args[0] | raw }
x = ${x}
quoted x = ${ x | raw }
dolor: $
explicit escaped dolor: $$
渲染到字符串时将得到:
arg0 = ok
quoted arg0 = "ok"
x = false
quoted x = "false"
dolor: $
explicit escaped dolor: $
访问路径
在访问payload时,可以使用路径进行深层访问。 路径由以下三种形式自由组合而成
- 下标访问:当要访问的值是array时,可用方括号括起的十进制整数访问,例如
[0]
、[1]
。 方括号和数字之间可以有空格。 - 名称访问:当要访问的值是map时,可以用点号加标识符访问,例如
.a
、.b
。 标识符的规则是字母或下划线开头,后接零或多个字母、数字、下划线。 当整个路径是以名称访问开头时,第一个点号可以省略。 - 带引号的名称访问:名称访问的另一种形式,如果名称不符合标识符的规则,可以用方括号括起一个单引号或双引号括起的属性名,在引号中,可以用反斜线转义特殊字符。 例如
["a"]
、['b']
方括号和引号之间可以有空格。
反斜线能转义的特殊字符如下:
\n
\r
\t
\\
:反斜线本身\'
:单引号本身,在双引号字符串中单引号可以不转义\"
:双引号本身,在单引号字符串中双引号可以不专一\u{0000}
:一个Unicode字符,其中0000
替换成Unicode值
如果下标访问的不是array,或者名称访问的不是 map,访问会出错。 如果下方访问时,下标溢出array长度,访问会出错。
例如,如果payload是{"a": {"b": ["ok"]}}
,以下几种路径都能得到"ok"
:
a.b[0]
.a.b[0]
["a"].b[0]
["a"]['b'][0]
特别地,空路径可以访问到整个payload本身。
path = [[first_segment] {, segment}];
first_segment = index_segment | attr_segment | first_attr_segment;
segment = index_segment | attr_segment;
index_segment = "[" index "]";
attr_segment = ".", unquoted_attr | "[" quoted_attr "]";
first_attr_segment = unquoted_attr;
index = INTEGER;
unquoted_attr = IDENTIFIER;
quoted_attr = SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING;