@guandata/guanetl 0.1.33 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +7 -1
- package/binaries/guanetl-darwin-arm64 +0 -0
- package/binaries/guanetl-darwin-x64 +0 -0
- package/binaries/guanetl-linux-arm64 +0 -0
- package/binaries/guanetl-linux-x64 +0 -0
- package/binaries/guanetl-win32-x64.exe +0 -0
- package/package.json +1 -1
- package/skills/guanetl/SKILL.md +20 -29
- package/skills/guanetl/references/ETL_AI_DEVELOP.md +52 -78
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## @guandata/guanetl 0.1.34 - 2026-09-09
|
|
4
|
+
|
|
5
|
+
- 新建 ETL 前要求明确目标目录并确认落位计划,成功后输出访问链接和存储路径。
|
|
6
|
+
- 执行等待改为按 ETL ID 跟踪,不再依赖管理员级全局任务列表;移除旧的全局 `task` 命令。
|
|
7
|
+
- DSL 新增类型化枚举与常量,并由生成的符号表统一校验,继续兼容已有字符串写法。
|
|
8
|
+
- 缺失或非法选项不再静默回退,静态检查会在提交前给出明确错误。
|
|
9
|
+
|
|
3
10
|
## @guandata/guanetl 0.1.33 - 2026-09-03
|
|
4
11
|
|
|
5
12
|
- 修复 Windows npm 全局安装场景下的 CLI 执行器定位,能够从 `.cmd` shim 解析实际原生程序。
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ guanetl save --dir <work_dir>
|
|
|
26
26
|
|
|
27
27
|
标准 ETL 写入闭环:`create/edit → export → preview → save → run --wait`。如果目标 ETL 依赖的智能 ETL 上游也需要刷新,可先用 `guanetl run <etl_id> --run-upstream --dry-run` 查看拓扑计划,再用 `guanetl run <etl_id> --run-upstream` 从最上游依次执行并等待完成。
|
|
28
28
|
|
|
29
|
-
多环境操作使用 `GUANCLI_PROFILE=<profile>` 固定当前进程链。写命令会先解析实际底层 CLI,再在 stderr 回显对应目标:`guancli` 显示 profile,`guancli-lite` 显示脱敏后的 `GUANCLI_BASE_URL` 主机,自定义 shim
|
|
29
|
+
多环境操作使用 `GUANCLI_PROFILE=<profile>` 固定当前进程链。写命令会先解析实际底层 CLI,再在 stderr 回显对应目标:`guancli` 显示 profile,`guancli-lite` 显示脱敏后的 `GUANCLI_BASE_URL` 主机,自定义 shim 不推测其环境语义。可先运行 `GUANCLI_PROFILE=<profile> guancli auth status` 核对环境名和 URL,不需要切换机器级默认 profile。
|
|
30
30
|
|
|
31
31
|
`preview` 返回 0 行时默认输出 warning 并保持兼容的成功退出码;自动化发布或要求输出必须有数据时使用 `preview --require-nonempty`,并仅在命令成功后继续 `save`。
|
|
32
32
|
|
|
@@ -51,6 +51,12 @@ guanetl install-skill
|
|
|
51
51
|
|
|
52
52
|
## 版本更新
|
|
53
53
|
|
|
54
|
+
### @guandata/guanetl 0.1.34
|
|
55
|
+
|
|
56
|
+
- 新建 ETL 要求指定目标目录并确认落位计划,完成后返回访问链接和存储路径。
|
|
57
|
+
- 执行等待改为按 ETL ID 跟踪,移除旧的全局任务查询入口。
|
|
58
|
+
- DSL 提供类型化枚举和常量;缺失或非法选项会在提交前明确失败。
|
|
59
|
+
|
|
54
60
|
### @guandata/guanetl 0.1.33
|
|
55
61
|
|
|
56
62
|
- 修复 Windows npm 全局安装场景下的 CLI 执行器定位问题。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/skills/guanetl/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: guanetl
|
|
3
|
-
description:
|
|
3
|
+
description: 当用户要新建、修改、调试、定时调度、立即执行、保存发布观远 BI / Guandata 的 ETL,或给出 ETL ID、dataFlowId、etl.go、meta.json、SQL 节点、dsId、节点报错时,优先使用这个 skill。即使用户只说"帮我改一下这个 ETL""这个 ETL 导出失败""新建一个观远 ETL""把这个 ETL 配成每天 2 点跑""保存后立即执行并等完成",也要主动使用。它把线上 ETL 拉到本地工作目录,约束 AI 只编辑 etl/ 下文件,并完成 create/edit → export → preview → save → run/schedule 闭环。查 ETL/数据集元信息走 guancli;数据源/数据集 CRUD 走 guands。
|
|
4
4
|
compatibility: "Requires Node.js 14+. Install via npm link --foreground-scripts (local) or npm install -g --foreground-scripts @guandata/guanetl (from internal Nexus registry) so the AI skill refresh result is visible. CLI command: guanetl."
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,7 +20,6 @@ compatibility: "Requires Node.js 14+. Install via npm link --foreground-scripts
|
|
|
20
20
|
- 用户给了 ETL ID / dataFlowId,希望直接拉下来改。
|
|
21
21
|
- 用户保存 ETL 后希望立即触发执行并等待完成。
|
|
22
22
|
- 用户希望配置 ETL 定时执行调度。
|
|
23
|
-
- 用户希望取消正在执行的 ETL 任务。
|
|
24
23
|
- 用户遇到 `export`、预览、保存失败,希望定位并修复。
|
|
25
24
|
- 用户提到 `etl.go`、`meta.json`、节点 ID、SQL 节点、输入输出数据集。
|
|
26
25
|
|
|
@@ -79,7 +78,7 @@ guancli ds get <ds_id>
|
|
|
79
78
|
guancli ds preview <ds_id> --limit 20
|
|
80
79
|
```
|
|
81
80
|
|
|
82
|
-
多环境操作时,使用 `GUANCLI_PROFILE=<profile>` 固定当前进程链的目标环境,不修改机器级默认 profile。写操作会先按 `GUANCLI_PATH → PATH 中 guancli → PATH 中 guancli-lite` 解析实际底层 CLI,再在 stderr 回显对应目标:`guancli` 显示 profile,`guancli-lite` 显示脱敏后的 `GUANCLI_BASE_URL` 主机,自定义 shim 则明确提示目标由该 CLI
|
|
81
|
+
多环境操作时,使用 `GUANCLI_PROFILE=<profile>` 固定当前进程链的目标环境,不修改机器级默认 profile。写操作会先按 `GUANCLI_PATH → PATH 中 guancli → PATH 中 guancli-lite` 解析实际底层 CLI,再在 stderr 回显对应目标:`guancli` 显示 profile,`guancli-lite` 显示脱敏后的 `GUANCLI_BASE_URL` 主机,自定义 shim 则明确提示目标由该 CLI 决定。执行前可用同一环境变量运行 `guancli auth status`,核对环境名和 URL:
|
|
83
82
|
|
|
84
83
|
```bash
|
|
85
84
|
GUANCLI_PROFILE=dev33 guancli auth status
|
|
@@ -171,7 +170,7 @@ guanetl run <etl_id> --wait # 可选:保存后触发执行并等待
|
|
|
171
170
|
### 新建 ETL
|
|
172
171
|
|
|
173
172
|
1. 先用 `guancli` 查输入数据集和字段。
|
|
174
|
-
2.
|
|
173
|
+
2. **创建前必须先与用户确认目录**:ETL 本体目录(`--parent-dir`,来自 ETL 目录树)必填,缺失 `create` 直接拒绝(缺省会被服务端静默落到根目录,不符合目录管理规范);输出数据集目录(`--output-parent-dir`,来自 DATA_SET 目录树)在 `create` 时可选(纯中间产物 ETL 可以没有输出),但只要 etl.go 里有输出节点,`save` 前就必须为每个输出指定目录,所以有输出的 ETL 也在创建前一并确认。先用目录树查出候选目录的路径和 id,把"ETL 放在 <路径>、输出数据集放在 <路径>"交给用户确认;目录不存在时用 `guanetl mkdir` / `guanetl mkdir-pair` 显式创建,CLI 不会自动建目录。用户明确要求放根目录时显式传根目录 id。两棵树相互独立,同名目录的 id 也不同,不能把 `guancli etl get` 输出节点里的 ParentDirId 当作 `create --parent-dir` 使用。`guancli workflow tree` 返回的是工作流/经典数据流目录树,也不能用于智能 ETL 的 `create --parent-dir`。
|
|
175
174
|
|
|
176
175
|
```bash
|
|
177
176
|
guancli etl tree --max-depth 2 # ETL 所在目录,用于 create --parent-dir
|
|
@@ -181,16 +180,18 @@ guanetl mkdir-pair "ODS" --etl-parent <etl_parent_id> --ds-parent <ds_parent_id>
|
|
|
181
180
|
guanetl rmdir <dirId> --yes # 删空的 ETL 目录(非空拒绝并列出内容;物理删除不可恢复)
|
|
182
181
|
```
|
|
183
182
|
|
|
184
|
-
3.
|
|
183
|
+
3. 执行(可先 `--dry-run` 看落位计划:只校验目录并打印"路径 (id)",不申请 dataFlowId):
|
|
185
184
|
|
|
186
185
|
```bash
|
|
187
|
-
guanetl create --name "ETL名称" --dir <work_dir>
|
|
186
|
+
guanetl create --name "ETL名称" --dir <work_dir> --parent-dir <etl_dir_id> --output-parent-dir <dataset_dir_id> --dry-run
|
|
188
187
|
guanetl create --name "ETL名称" --dir <work_dir> --parent-dir <etl_dir_id> --output-parent-dir <dataset_dir_id>
|
|
189
188
|
```
|
|
190
189
|
|
|
191
190
|
`create` 只向服务端申请 `dataFlowId` 并初始化本地工作区,不代表完整 ETL 已持久化。首次 `save` 成功前,服务端 `get/edit/move` 可能返回 ETL 不存在;完成 `etl.go -> export -> preview -> save` 后再执行这些服务端操作。
|
|
192
191
|
|
|
193
192
|
`create` / `save` 会校验目录 id 类型:`--parent-dir` 必须来自 ETL 树,输出数据集目录必须来自 DATA_SET 树。只有在确认目录 id 正确但当前账号无法读取目录树时,才使用 `--skip-dir-check`。
|
|
193
|
+
|
|
194
|
+
**`save` 的目录规则**:每个新建输出节点都必须带 DATA_SET 目录(`BasicOutputDatasetInDir`),无目录的新输出会在 direct-save 前被拒绝;首次保存还要求 ETL 本体目录(来自 `create --parent-dir`)。**已物化的输出(线上已有 dsId)一律沿用服务端绑定的 dsId 和目录**,落位计划按服务端 `dataSource` 展示并标注"不会改动",本地 `parentDirId` 意图不会覆盖它;要换目录属于替换/迁移,走既有的输出绑定风险流程。绑定已有数据集的增量输出不要求目录。`save --dry-run` 和真实保存都会打印"落位计划"(ETL 本体与每个输出的目标目录路径 + id,JSON 报告在 `saveImpact.placements`);修改已保存 ETL 时若落位计划中出现新目录或新输出,同样要先把落位计划交给用户确认再去掉 `--dry-run`。保存成功后会逐个输出数据集打印 `路径:` 与 `链接:`(尚未创建的输出只有路径,链接要等 `run --wait` 成功后 dsId 固定才会给出)。
|
|
194
195
|
如果错误提示目录 id 属于 `MASTER_FLOW`,说明拿到了工作流/经典数据流目录 id;智能 ETL 改用 `guancli etl tree` 查询,工作流/数据流创建改用 `guanwf create --parent-dir`。
|
|
195
196
|
|
|
196
197
|
移动已有智能 ETL 用 `move`,目标目录同样必须来自 `guancli etl tree`。可先加 `--dry-run` 查看将提交的 `/api/etl/move` 请求体;批量移动直接追加多个 ETL ID:
|
|
@@ -232,13 +233,13 @@ guanetl move <etl_id1> <etl_id2> --dir-id <etl_dir_id>
|
|
|
232
233
|
|
|
233
234
|
> **触发成功 ≠ ETL 执行成功**。`run` 返回 `✓ ETL 执行已触发` 仅表示后端接受了执行请求,ETL 可能在运行中失败。
|
|
234
235
|
|
|
235
|
-
- 不加 `--wait` 时,`run`
|
|
236
|
-
- 加 `--wait` 后,CLI
|
|
236
|
+
- 不加 `--wait` 时,`run` 只触发后返回。**再次执行 `run` 会重新触发一次执行**(增量写入类 ETL 会重复产出数据);只有 ETL 仍在运行时,`run <etl_id> --wait` 才会被服务端拒绝重复提交并改为跟随当前执行。
|
|
237
|
+
- 加 `--wait` 后,CLI 会等待本次执行直到终态(FINISHED / FAILED / CANCELED),FAILED 时会展示真实错误消息。任务 ID 只在进程内部使用,不对外输出。
|
|
237
238
|
- 需要递归刷新智能 ETL 上游链路时,用 `run <etl_id> --run-upstream`。CLI 会读取目标 ETL 的输入数据集,按数据集的生产 ETL 继续向上解析,生成拓扑顺序后从最上游依次执行,包含目标 ETL;每个 ETL 都会等待终态,任一失败即停止并报告失败节点。
|
|
238
239
|
- 不确定上游范围时,先用 `run <etl_id> --run-upstream --dry-run` 输出执行计划;dry-run 只读拓扑,不触发任何 ETL。
|
|
239
240
|
- 触发前,`run` 会检查直接上游数据集状态;若发现上游处于 `FAILED`/`失败` 态,会先输出警告但继续触发执行。确认不需要检查时可加 `--skip-upstream-check`。
|
|
240
241
|
- `run <etl_id>` 还会读取服务端 edit 结构,按 JOIN 键类型组合提示隐式 coercion、精度或日期时间风险;`--run-upstream` 会在每个计划节点执行前做同样检查,检查失败不阻断执行。`preview` / `save` 会阻断明确不可用于行级 JOIN 的 aggregation/window 字段,并按服务端字段身份解析 JOIN predicate alias。批量自动化确认不需要运行前检查时可加 `--skip-join-type-check`。
|
|
241
|
-
- 如果不加 `--wait`
|
|
242
|
+
- 如果不加 `--wait` 后想知道结果:用只读的 `guancli etl get <etl_id>`,默认输出的「最近执行」段落给出 status / 提交 / 开始 / 结束时间,不要再次 `run`。
|
|
242
243
|
|
|
243
244
|
### 排查执行失败
|
|
244
245
|
|
|
@@ -246,14 +247,11 @@ guanetl move <etl_id1> <etl_id2> --dir-id <etl_dir_id>
|
|
|
246
247
|
# 1. 触发执行并等待完成
|
|
247
248
|
guanetl run <etl_id> --wait
|
|
248
249
|
|
|
249
|
-
# 2.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
# 3. 等待任务完成并查看真实错误
|
|
253
|
-
guanetl task wait <taskId>
|
|
250
|
+
# 2. 如果已经触发但不知道结果,用只读元信息查看最近执行(不要再次 run,那会重新触发执行)
|
|
251
|
+
guancli etl get <etl_id>
|
|
254
252
|
|
|
255
|
-
#
|
|
256
|
-
|
|
253
|
+
# 3. 只有确认 ETL 仍在运行时,run --wait 才会跟随当前执行而不重复触发
|
|
254
|
+
guanetl run <etl_id> --wait
|
|
257
255
|
```
|
|
258
256
|
|
|
259
257
|
常见失败原因:
|
|
@@ -320,19 +318,11 @@ guanetl schedule <etl_id> --trigger upstream-all --inputs <dsId1>,<dsId2>
|
|
|
320
318
|
|
|
321
319
|
**调度频率限制**:定时模式最高 5 分钟一次,低于 1 小时会输出性能警告。
|
|
322
320
|
|
|
323
|
-
##
|
|
321
|
+
## 执行结果与任务
|
|
324
322
|
|
|
325
|
-
-
|
|
326
|
-
- 并行触发多个 ETL
|
|
327
|
-
-
|
|
328
|
-
|
|
329
|
-
```bash
|
|
330
|
-
guanetl task status <taskId> # 查询任务状态
|
|
331
|
-
guanetl task wait <taskId> # 等待任务完成
|
|
332
|
-
guanetl task wait <t1> <t2> --timeout 600 # 等待多个任务
|
|
333
|
-
guanetl task cancel <taskId> # 取消执行中的任务
|
|
334
|
-
guanetl task cancel <t1> <t2> # 批量取消多个任务
|
|
335
|
-
```
|
|
323
|
+
- 没有独立的任务命令:不支持按任务 ID 查询、等待或取消任务。等待一律以 ETL ID 为入口(`run <etl_id> --wait`);查历史结果用只读的 `guancli etl get <etl_id>`。
|
|
324
|
+
- 并行触发多个 ETL 时,分别对每个 ETL 执行 `run <etl_id> --wait`(可并行起多个进程);`--run-upstream` 会按拓扑顺序逐个等待。
|
|
325
|
+
- 需要停止正在执行的 ETL 时,请到 BI 管理中心任务详情操作;CLI 不提供取消能力。
|
|
336
326
|
|
|
337
327
|
## 硬约束
|
|
338
328
|
|
|
@@ -378,7 +368,8 @@ guanetl task cancel <t1> <t2> # 批量取消多个任务
|
|
|
378
368
|
|
|
379
369
|
至少说明这四件事:
|
|
380
370
|
|
|
381
|
-
- 处理的是哪个 ETL /
|
|
371
|
+
- 处理的是哪个 ETL / 工作目录,并附上 `edit`/`save`/`move`/`mkdir` 输出里的 `链接:` 和 `路径:`(ETL 详情页为 `<BI 地址>/data-flow/<etlId>/details/datasets`,目录为 `<BI 地址>/data-center/data-flow/<dirId>`),让用户能直接在 BI 中打开而不是拿着 ID 自己找。`create` 申请到的 dataFlowId 在首次 `save` 成功前服务端尚未持久化,此时不要编造链接。
|
|
372
|
+
- 每个输出数据集的 `路径:` 和 `链接:`,取自 `save` 成功后的"输出数据集落位"段或 `run --wait` 成功后的逐输出输出(数据集详情页为 `<BI 地址>/data-source/<dsId>/details/overview`)。`save` 后仍标注"尚未创建"的输出不要编造链接,改为说明需要 `run --wait` 成功后再取。
|
|
382
373
|
- 改了哪些 `etl/` 文件。
|
|
383
374
|
- `export`、`preview`、`save`、`run` 哪些已经成功。
|
|
384
375
|
- 如果没闭环,卡在哪一步,下一步最小动作是什么。
|
|
@@ -52,9 +52,9 @@ func DefineETL() []Node
|
|
|
52
52
|
|
|
53
53
|
```go
|
|
54
54
|
orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
|
|
55
|
-
BasicField("user_id",
|
|
56
|
-
BasicField("amount",
|
|
57
|
-
BasicField("order_date",
|
|
55
|
+
BasicField("user_id", FieldTypeSTRING),
|
|
56
|
+
BasicField("amount", FieldTypeDOUBLE),
|
|
57
|
+
BasicField("order_date", FieldTypeDATE),
|
|
58
58
|
}, Position{X: 100, Y: 100})
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -74,19 +74,19 @@ orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
|
|
|
74
74
|
calc := BasicCalculator("id_1002", "解析展示格式度量", inputA, []Formula{
|
|
75
75
|
{
|
|
76
76
|
Name: "下单转化率_数值",
|
|
77
|
-
Type:
|
|
77
|
+
Type: FieldTypeDOUBLE,
|
|
78
78
|
Expr: "CAST(REPLACE([下单转化率], \"%\", \"\") AS DOUBLE) / 100",
|
|
79
79
|
Key: "formula_order_rate_value",
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
Name: "营业时长_分钟",
|
|
83
|
-
Type:
|
|
83
|
+
Type: FieldTypeDOUBLE,
|
|
84
84
|
Expr: "CAST(IF(REGEXP_EXTRACT([营业时长], \"([0-9.]+)小时\", 1) = \"\", \"0\", REGEXP_EXTRACT([营业时长], \"([0-9.]+)小时\", 1)) AS DOUBLE) * 60 + CAST(IF(REGEXP_EXTRACT([营业时长], \"([0-9.]+)分\", 1) = \"\", \"0\", REGEXP_EXTRACT([营业时长], \"([0-9.]+)分\", 1)) AS DOUBLE)",
|
|
85
85
|
Key: "formula_open_minutes",
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
Name: "店铺分_数值",
|
|
89
|
-
Type:
|
|
89
|
+
Type: FieldTypeDOUBLE,
|
|
90
90
|
Expr: "CAST([店铺分] AS DOUBLE)",
|
|
91
91
|
Key: "formula_store_score_value",
|
|
92
92
|
},
|
|
@@ -122,7 +122,7 @@ BasicOutputDataset(id, name string, source Node, outputDsName string, position P
|
|
|
122
122
|
BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
`BasicOutputDatasetInDir`
|
|
125
|
+
**新建输出节点必须用 `BasicOutputDatasetInDir`** 并传入 `guanetl create --output-parent-dir` 确认过的 DATA_SET 目录 id;`save` 会拒绝没有目录的新输出(服务端对空目录不报错而是静默落到根目录,不符合目录管理规范)。无目录版本的 `BasicOutputDataset` 只用于 `edit` 回读已物化输出时的往返兼容(目录已固化在服务端 dataSource 中),不要在新增输出时使用。目录 ID 可通过 `guancli ds tree` 获取,不存在时用 `guanetl mkdir --type DATA_SET` 显式创建。
|
|
126
126
|
|
|
127
127
|
新建 ETL 时有两类目录 id,不能混用:
|
|
128
128
|
|
|
@@ -147,20 +147,20 @@ BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, positi
|
|
|
147
147
|
### 筛选
|
|
148
148
|
|
|
149
149
|
```go
|
|
150
|
-
BasicFilterRows(id, name string, source Node, combineType
|
|
150
|
+
BasicFilterRows(id, name string, source Node, combineType CombineType, conditions []FilterCondition, position Position)
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
- `combineType`
|
|
154
|
-
-
|
|
153
|
+
- `combineType` 用常量 `CombineTypeAND` / `CombineTypeOR`
|
|
154
|
+
- 操作符用 `FilterOp*` 常量:`FilterOpEQ` `FilterOpNE` `FilterOpLT` `FilterOpLE` `FilterOpGT` `FilterOpGE` `FilterOpIN` `FilterOpBT` `FilterOpCONTAINS` `FilterOpNOTCONTAINS` `FilterOpSTARTSWITH` `FilterOpNOTSTARTSWITH` `FilterOpENDSWITH` `FilterOpNOTENDSWITH`
|
|
155
155
|
- 当前 `BasicFilterRows` 不支持 `IS_NULL` / `NOT_NULL`;null 过滤请改用 SQL 节点(如 ``WHERE `列名` IS NULL`` / ``IS NOT NULL``)。
|
|
156
156
|
|
|
157
157
|
### 等值 JOIN
|
|
158
158
|
|
|
159
159
|
```go
|
|
160
|
-
BasicJoinData(id, name string, leftSource, rightSource Node, joinType
|
|
160
|
+
BasicJoinData(id, name string, leftSource, rightSource Node, joinType JoinType, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
- `joinType`
|
|
163
|
+
- `joinType` 用常量 `JoinTypeINNER` `JoinTypeLEFTOUTER` `JoinTypeRIGHTOUTER` `JoinTypeFULLOUTER`
|
|
164
164
|
|
|
165
165
|
### 聚合
|
|
166
166
|
|
|
@@ -168,7 +168,7 @@ BasicJoinData(id, name string, leftSource, rightSource Node, joinType string, jo
|
|
|
168
168
|
BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
-
|
|
171
|
+
- 聚合类型用常量 `AggrTypeSUM` `AggrTypeCOUNT` `AggrTypeCOUNTDISTINCT` `AggrTypeMIN` `AggrTypeMAX` `AggrTypeAVG` `AggrTypeFIRSTNOTNULL`
|
|
172
172
|
|
|
173
173
|
### 计算列
|
|
174
174
|
|
|
@@ -181,14 +181,14 @@ BasicCalculator(id, name string, source Node, formulas []Formula, position Posit
|
|
|
181
181
|
```go
|
|
182
182
|
Formula{
|
|
183
183
|
Name: "订单金额等级",
|
|
184
|
-
Type:
|
|
184
|
+
Type: FieldTypeSTRING,
|
|
185
185
|
Expr: "IF([金额] >= 1000, \"大额\", \"普通\")",
|
|
186
186
|
Key: "formula_amount_level",
|
|
187
187
|
}
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
- `Name` 是新增列名。
|
|
191
|
-
- `Type`
|
|
191
|
+
- `Type` 是新增列类型,用 `FieldType*` 常量:`FieldTypeINT` `FieldTypeDOUBLE` `FieldTypeSTRING` `FieldTypeTIMESTAMP` `FieldTypeLONG` `FieldTypeSHORT` `FieldTypeFLOAT` `FieldTypeDATE` `FieldTypeBOOL` `FieldTypeDECIMAL` `FieldTypeARRAY`。
|
|
192
192
|
- `Expr` 是服务端公式表达式,字段引用必须写成 `[字段名]`。
|
|
193
193
|
- `Key` 在同一个 `CALCULATOR` 节点内必须唯一;建议用稳定字符串,不要留空。
|
|
194
194
|
|
|
@@ -217,13 +217,13 @@ Formula{
|
|
|
217
217
|
calc := BasicCalculator("id_1002", "新增计算列", inputA, []Formula{
|
|
218
218
|
{
|
|
219
219
|
Name: "含税金额",
|
|
220
|
-
Type:
|
|
220
|
+
Type: FieldTypeDOUBLE,
|
|
221
221
|
Expr: "ROUND([金额] * 1.13, 2)",
|
|
222
222
|
Key: "formula_tax_amount",
|
|
223
223
|
},
|
|
224
224
|
{
|
|
225
225
|
Name: "门店前缀",
|
|
226
|
-
Type:
|
|
226
|
+
Type: FieldTypeSTRING,
|
|
227
227
|
Expr: "LEFT([门店ID], 2)",
|
|
228
228
|
Key: "formula_store_prefix",
|
|
229
229
|
},
|
|
@@ -264,14 +264,14 @@ import . "guanetl/internal/framework"
|
|
|
264
264
|
|
|
265
265
|
func DefineETL() []Node {
|
|
266
266
|
inputA := BasicInputDataset("id_1001", "输入A", "ds_a", []Field{
|
|
267
|
-
BasicField("id",
|
|
267
|
+
BasicField("id", FieldTypeSTRING),
|
|
268
268
|
}, Position{X: 100, Y: 100})
|
|
269
269
|
|
|
270
270
|
transformed := BasicFilterRows(
|
|
271
271
|
"id_1002",
|
|
272
272
|
"筛选A",
|
|
273
273
|
inputA,
|
|
274
|
-
|
|
274
|
+
CombineTypeAND,
|
|
275
275
|
[]FilterCondition{},
|
|
276
276
|
Position{X: 320, Y: 100},
|
|
277
277
|
)
|
|
@@ -292,9 +292,9 @@ import . "guanetl/internal/framework"
|
|
|
292
292
|
|
|
293
293
|
func DefineETL() []Node {
|
|
294
294
|
orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
|
|
295
|
-
BasicField("门店",
|
|
296
|
-
BasicField("订单号",
|
|
297
|
-
BasicField("销售额",
|
|
295
|
+
BasicField("门店", FieldTypeSTRING),
|
|
296
|
+
BasicField("订单号", FieldTypeSTRING),
|
|
297
|
+
BasicField("销售额", FieldTypeDOUBLE),
|
|
298
298
|
}, Position{X: 100, Y: 120})
|
|
299
299
|
|
|
300
300
|
detail := BasicSelectColumns("id_1002", "订单明细", orders, []ColumnSetting{
|
|
@@ -308,10 +308,10 @@ func DefineETL() []Node {
|
|
|
308
308
|
"门店汇总",
|
|
309
309
|
orders,
|
|
310
310
|
[]GroupByColumn{
|
|
311
|
-
BasicGroupByColumn("门店",
|
|
311
|
+
BasicGroupByColumn("门店", FieldTypeSTRING),
|
|
312
312
|
},
|
|
313
313
|
[]AggregationColumn{
|
|
314
|
-
BasicAggregationColumnWithAlias("销售额",
|
|
314
|
+
BasicAggregationColumnWithAlias("销售额", FieldTypeDOUBLE, AggrTypeSUM, "销售额合计"),
|
|
315
315
|
},
|
|
316
316
|
Position{X: 340, Y: 200},
|
|
317
317
|
)
|
|
@@ -378,12 +378,12 @@ BasicInputDataset(id, name, inputDsID string, fields []Field, position Position)
|
|
|
378
378
|
BasicOutputDataset(id, name string, source Node, outputDsName string, position Position)
|
|
379
379
|
BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
|
|
380
380
|
BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, position Position)
|
|
381
|
-
BasicFilterRows(id, name string, source Node, combineType
|
|
382
|
-
BasicJoinData(id, name string, leftSource, rightSource Node, joinType
|
|
381
|
+
BasicFilterRows(id, name string, source Node, combineType CombineType, conditions []FilterCondition, position Position)
|
|
382
|
+
BasicJoinData(id, name string, leftSource, rightSource Node, joinType JoinType, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
|
|
383
383
|
BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
|
|
384
384
|
BasicCalculator(id, name string, source Node, formulas []Formula, position Position)
|
|
385
385
|
BasicRemoveDuplicates(id, name string, source Node, columnNames []string, position Position)
|
|
386
|
-
BasicAppendRows(id, name string, sources []Node, unionType
|
|
386
|
+
BasicAppendRows(id, name string, sources []Node, unionType UnionType, schemaSource string, position Position)
|
|
387
387
|
BasicSqlScript(id, name string, sources []Node, sql string, position Position)
|
|
388
388
|
```
|
|
389
389
|
|
|
@@ -407,18 +407,18 @@ NewSqlScript(id, name string, sources []Node, config SqlScriptConfig)
|
|
|
407
407
|
### 高频辅助函数
|
|
408
408
|
|
|
409
409
|
```go
|
|
410
|
-
BasicField(name, fieldType
|
|
410
|
+
BasicField(name string, fieldType FieldType)
|
|
411
411
|
BasicColumnSetting(name string)
|
|
412
412
|
BasicJoinColumnPair(leftColumn, rightColumn string)
|
|
413
413
|
BasicJoinOutputFromLeft(columnName string)
|
|
414
414
|
BasicJoinOutputFromRight(columnName string)
|
|
415
415
|
BasicJoinOutputFromLeftWithAlias(columnName, alias string)
|
|
416
416
|
BasicJoinOutputFromRightWithAlias(columnName, alias string)
|
|
417
|
-
BasicGroupByColumn(name, columnType
|
|
418
|
-
BasicGroupByColumnWithAlias(name, columnType, newName string)
|
|
419
|
-
BasicAggregationColumn(name, columnType, aggregationType
|
|
420
|
-
BasicAggregationColumnWithAlias(name, columnType, aggregationType, newName string)
|
|
421
|
-
BasicFilterCondition(columnName, operator
|
|
417
|
+
BasicGroupByColumn(name string, columnType FieldType)
|
|
418
|
+
BasicGroupByColumnWithAlias(name string, columnType FieldType, newName string)
|
|
419
|
+
BasicAggregationColumn(name string, columnType FieldType, aggregationType AggregationType)
|
|
420
|
+
BasicAggregationColumnWithAlias(name string, columnType FieldType, aggregationType AggregationType, newName string)
|
|
421
|
+
BasicFilterCondition(columnName string, operator FilterOperator, filterValues []FilterValue)
|
|
422
422
|
BasicFilterValue(value string)
|
|
423
423
|
BasicFilterColumnValue(columnName string)
|
|
424
424
|
ReadSQLFile(filename string)
|
|
@@ -428,55 +428,29 @@ ReadSQLFile(filename string)
|
|
|
428
428
|
|
|
429
429
|
```go
|
|
430
430
|
type Formula struct {
|
|
431
|
-
Name string
|
|
432
|
-
Type
|
|
433
|
-
Expr string
|
|
434
|
-
Key string
|
|
431
|
+
Name string `json:"name"`
|
|
432
|
+
Type FieldType `json:"type"`
|
|
433
|
+
Expr string `json:"expr"`
|
|
434
|
+
Key string `json:"key"`
|
|
435
435
|
}
|
|
436
436
|
```
|
|
437
437
|
|
|
438
|
-
###
|
|
438
|
+
### 枚举常量(优先使用常量)
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
JOIN 类型、筛选组合、操作符、聚合类型、UNION 类型、字段/公式类型都是命名类型,
|
|
441
|
+
框架提供同名常量,命名规则是 `<类型前缀> + 去掉下划线的值`(`COUNT_DISTINCT` → `AggrTypeCOUNTDISTINCT`)。
|
|
442
|
+
**优先写常量**:常量名拼错会在 `export` 求值脚本时直接报 `undefined`,而字符串字面量拼错要等到图校验阶段才发现。
|
|
443
|
+
字符串字面量(`"INNER"`)仍然可用(存量脚本无需修改,`guanetl lint` 会提示改用常量);
|
|
444
|
+
把 `string` 变量传给枚举参数需要显式转换,例如 `JoinType(v)`。
|
|
441
445
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
- `CONTAINS` `NOT_CONTAINS`
|
|
452
|
-
- `STARTSWITH` `NOT_STARTSWITH`
|
|
453
|
-
- `ENDSWITH` `NOT_ENDSWITH`
|
|
454
|
-
|
|
455
|
-
当前限制:
|
|
456
|
-
|
|
457
|
-
- `BasicFilterRows` 暂不支持 `IS_NULL` / `NOT_NULL`;null 过滤请使用 `BasicSqlScript`。
|
|
458
|
-
|
|
459
|
-
JOIN 类型:
|
|
460
|
-
|
|
461
|
-
- `INNER`
|
|
462
|
-
- `LEFT_OUTER`
|
|
463
|
-
- `RIGHT_OUTER`
|
|
464
|
-
- `FULL_OUTER`
|
|
465
|
-
|
|
466
|
-
聚合类型:
|
|
467
|
-
|
|
468
|
-
- `SUM`
|
|
469
|
-
- `COUNT`
|
|
470
|
-
- `COUNT_DISTINCT`
|
|
471
|
-
- `MIN`
|
|
472
|
-
- `MAX`
|
|
473
|
-
- `AVG`
|
|
474
|
-
- `FIRST_NOT_NULL`
|
|
475
|
-
|
|
476
|
-
APPEND_ROWS unionType:
|
|
477
|
-
|
|
478
|
-
- `INCLUDE_SHARED`
|
|
479
|
-
- `INCLUDE_ALL`
|
|
480
|
-
- `INCLUDE_FROM`
|
|
446
|
+
| 枚举 | 常量 | 说明 |
|
|
447
|
+
|---|---|---|
|
|
448
|
+
| 筛选组合 `CombineType` | `CombineTypeAND` `CombineTypeOR` | |
|
|
449
|
+
| 筛选操作符 `FilterOperator` | `FilterOpEQ` `FilterOpNE` `FilterOpLT` `FilterOpLE` `FilterOpGT` `FilterOpGE` `FilterOpIN` `FilterOpBT` `FilterOpCONTAINS` `FilterOpNOTCONTAINS` `FilterOpSTARTSWITH` `FilterOpNOTSTARTSWITH` `FilterOpENDSWITH` `FilterOpNOTENDSWITH` | `FilterOpISNULL` / `FilterOpNOTNULL` 当前后端 FILTER_ROWS 不支持,null 过滤请用 `BasicSqlScript` |
|
|
450
|
+
| 筛选值类型 `FilterValueType` | `FilterValueTypeVALUE` `FilterValueTypeCOLUMN` | 通常经 `BasicFilterValue` / `BasicFilterColumnValue` 间接使用 |
|
|
451
|
+
| JOIN 类型 `JoinType` | `JoinTypeINNER` `JoinTypeLEFTOUTER` `JoinTypeRIGHTOUTER` `JoinTypeFULLOUTER` | 服务端把 FULL_OUTER 记为 OUTER,框架自动转换 |
|
|
452
|
+
| 聚合类型 `AggregationType` | `AggrTypeSUM` `AggrTypeCOUNT` `AggrTypeCOUNTDISTINCT` `AggrTypeMIN` `AggrTypeMAX` `AggrTypeAVG` `AggrTypeFIRSTNOTNULL` | 服务端记为 SUM/CNT/CNT_DISTINCT/MIN/MAX/AVG/NUL,框架自动转换 |
|
|
453
|
+
| APPEND_ROWS `UnionType` | `UnionTypeINCLUDESHARED` `UnionTypeINCLUDEALL` `UnionTypeINCLUDEFROM` | |
|
|
454
|
+
| 字段/公式类型 `FieldType` | `FieldTypeINT` `FieldTypeDOUBLE` `FieldTypeSTRING` `FieldTypeTIMESTAMP` `FieldTypeLONG` `FieldTypeSHORT` `FieldTypeFLOAT` `FieldTypeDATE` `FieldTypeBOOL` `FieldTypeDECIMAL` `FieldTypeARRAY` | `Formula.Type` 只能取这些值;`BasicInputDataset` 的字段类型来自服务端,可能超出该列表,按 `guancli ds get` 的结果照写即可 |
|
|
481
455
|
|
|
482
456
|
`BasicAppendRows` 会按列名对齐各输入源。参与拼接的同名列类型必须一致;例如一侧为 `DATE`、另一侧为 `LONG` 会在 `export` 阶段报错。遇到冲突时,先用 `BasicCalculator` / `BasicSelectColumns` 把各源字段统一类型或移除不用的冲突列,再执行行拼接。
|