# 链路式编写入门与实战

## 简介以及使用场景

链路式，英文即 **D**ata **M**odel **E**xpression (DME)， 有以下几个使用场景

1. 插件的注册 xml 当中定义插件接口入参，手动输入
2. Wecube 的 web 界面中定义插件接口入参，手动输入（包含自动提示）
3. 在 WeCube 流程引擎的内部执行相关的流程编排



## 词法及语法解释

相关表达式的开发引入 [ANTLR4](https://www.antlr.org/) 包，相关语法文件可参考源代码中的`DataModel.g4`文件

### 词法

词法输入支持大小写+数字(必须以字母开头)及特殊字符(包含!@#$%^&*_-)

| 关键词   | 解释                                               |
| -------- | -------------------------------------------------- |
| ID       | 词法基本单位                                       |
| pkg      | 包名                                               |
| ety      | 表名                                               |
| attr     | 属性名                                             |
| entity   | 逻辑Entity（带有包名和表名, 由 `pkg:ety`组成）     |
| fwd_node | 前向节点 (用于referenceTo，由 `Entity.attr` 组成)  |
| bwd_node | 后向节点 (用于referenceBy, 由 `(attr)Entity` 组成) |

### 语法

链路式由 `Entity`, `fwd_node`, `bwd_node` 以及 `op` 构成

| 语法名称 | 解释                                                         |
| -------- | ------------------------------------------------------------ |
| to       | referenceTo: `>`                                             |
| by       | referenceBy: `~`                                             |
| fetch    | 最后的表达式需要获取具体的 `attr`                            |
| link     | 用于描述一张表连接到另一张表的行为，可由 `fwd_node` `to` `entity`，`entity` `by` `bwd_node`, `link` `to` `entity` 和 `link` `by` `bwd_node`构成（具有自反性） |
| route    | 由`link` `fetch` 或 `entity` `fetch` （用于定位没有 `link` 只有 `fetch` 的情况）组成，表示最后的数据通路 |

表达式的最后结果是一个 `route`，WeCube 将对 每一个 `link `以及最后的 `route` 做解析，并且生成链式的请求获取数据。



## 入参与出参示例

在以下例子中，我们先忽略跨插件包的情况，即此处的 `Entity` 忽略 `Package`

例如，有一个六张表的关系（其中箭头连线代表外键关系），如图：

![image](https://user-images.githubusercontent.com/5387479/68387923-70f64300-019a-11ea-9f82-320de666e642.png)


则有以下三种情况：

1. 仅有单点，无 referenceTo 与 referenceBy关系

| 表达式 | 根   | ID值 | 输出 |
| ------ | ---- | ---- | ---- |
| `A.a1` | A    | 1    | a11  |
| `B.b3` | B    | a11  | EE   |
| `D.d2` | D    | DD   | dz   |

2. 仅有 referenceTo 关系

| 表达式           | 根   | ID值 | 输出 |
| ---------------- | ---- | ---- | ---- |
| `A.a1>B.b2`      | A    | 1    | BB   |
| `A.a1>B.b1>C.c1` | A    | 2    | o    |
| `A.a1>B.b2>D.d2` | A    | 1    | dy   |

3. 仅有 referenceBy 关系

| 表达式             | 根   | ID值 | 输出 |
| ------------------ | ---- | ---- | ---- |
| `B~(a1)A.a2`       | B    | a11  | a21  |
| `D~(b2)B.id`       | D    | DD   | a12  |
| `D~(b2)B~(a1)A.a2` | D    | BB   | a21  |
| `C~(b1)B~(a1)A.id` | C    | CC   | 2    |

4. 混合的情况

| 表达式                 | 根   | ID值 | 输出 |
| ---------------------- | ---- | ---- | ---- |
| `A.a1>B~(id)E.e2`      | A    | 1    | ey   |
| `B~(a1)A.a2>F.f2`      | B    | a12  | fz   |
| `F~(a2)A.a1>B.b2>D.d2` | F    | a11  | dy   |

## 链路式实战

在实战中，我们使用 WeCMDB 作为实例，用于展示如何通过 DME 及根节点的 ID 值获取具体数据

### 无连接情况 

即无 reference to 或 reference by 的情况

`we-cmdb:system_design.code`，根ID值为`0001_0000000001`

语义：

1. 读取wecmdb:system_design 这张表的 ID 为 `0001_0000000001`的数据
2. 取出 `code` 这一列的值，结果为 `EDP`



`wecmdb:unit.key_name`，根 ID 为`0008_0000000003`

语义：

1. wecmdb:unit 这张表的 ID 为 `0008_0000000003`的数据
2. 取出 `key_name` 这一列的值，结果为`EDP-CORE_PRD-APP`



### 单连接的情况

#### 单ReferenceTo

`wecmdb:subsys_design.system_design>wecmdb:system_design.code`, 根 ID 值为`0002_0000000006`

语义：

1. `wecmdb:subsys_design` 这张表的 ID 为 `0002_0000000006`的数据
2. 取出  `system_design` 这一列的值
3. 找出 2 所引用的来自`wecmdb:system_design`的记录
4. 找出 `code` 这一列的值，结果为 `EDP`



#### 单ReferenceBy

`wecmdb:service_design~(service_design)wecmdb:invoke_design.key_name`, 根 ID 值为 `0004_0000000001`

语义：

1.  `wecmdb:invoke_design` 表中，列`service_design`引用了`wecmdb:service_design`的` ID`列（即后者被前者引用），其`ID`数据为`0004_0000000001`

2. 在1中找出这行数据，取出其中列名为`key_name`的数据，结果为有两个:

   1. EDP-ADMCORE-APP_SYNC_INVOC_EDP-CORE-APP-SER1
   2. EDP-ADMBATCH-APP_SYNC_INVOC_EDP-CORE-APP-SER1

   

### 多连接情况

多连接的语义与单连接情况相同，下面给出具体例子

#### 多 ReferenceTo 与多 ReferenceBy

`wecmdb:subsys.subsys_design>wecmdb:subsys_design.system_design>wecmdb:system_design.key_name`, 根 ID 值为 `0007_0000000001`, 返回值：`ECIF`



`wecmdb:subsys~(subsys)wecmdb:unit~(unit)wecmdb:running_instance.id`, 根 ID 值为`0007_0000000001`, 返回值为: `0015_0000000001`



### 混合情况

`wecmdb:subsys~(subsys)wecmdb:unit.unit_design>wecmdb:unit_design.subsys_design>wecmdb:subsys_design.key_name`, 根 ID 值为 `0007_0000000001`, 返回两行数据，提取出 `key_name`后两列数据相同：

1. ECIF-CORE
2. ECIF-CORE



`wecmdb:zone_design~(zone_design2)wecmdb:zone_link_design~(zone_link_design)wecmdb:zone_link.zone1>wecmdb:zone.key_name`, 根 ID 值为 `0023_0000000004`, 返回两行数据，提取出 `key_name`后两列数据为：

1. PRD-GZ1-MGMT
2. PRD-GZ1-PARTNERNET

