GDIM数据更新 (Data Update)

本章说明如何为 GDIM 表格模板配置 中表格属性里的 「自定义数据更新配置」 编写 Pipeline。 平台界面和部分文档里,同一功能也可能写作 「表格数据更新」 或 「数据更新」。

这类应用和普通「把结果立刻写回 GDIM 表」的 Pipeline 不是同一种运行契约: GDIM 先把 Pipeline 当 预览 跑一遍,用户核对返回的表之后,再由 前端 写入。 上传的 .pipe 在这次预览运行里 不得 自己写库。

Note

平台界面称「数字模型」,SDK 中仍多写作 template / 「模板」,见 术语对照。

适用场景

把 Pipeline 做成「自定义数据更新配置」应用,通常是为了:

  • 用一张源表的字段去更新或补齐另一张目标表(例如把标准地层的岩性、密实度同步到岩土参数建议值表);

  • 先让用户在界面上看到合并后的预览表,确认后再写入;

  • 在 子表 上只更新当前主表主键范围内的行(此时还要配合 auto_bind="pk_field")。

如果目标是脚本或普通应用 运行时立即写库,继续用 GdimTableWriter 的默认行为(auto_run=True)即可, 见 新手入门 (Getting Started) 和 modules.writers 模块帮助 中 GdimTableWriter 的普通写入示例。 不要把本章的预览约定套到那种 Pipeline 上。

平台如何运行

  1. 用户在数字模型里配置「自定义数据更新」,并挂上你上传的 .pipe。

  2. GDIM 运行 Pipeline 做预览,读取 return_results_config 指定的表端口,把结果展示给用户。

  3. 用户核对后点击确认,**由前端写入**目标表;预览这次运行里 Pipeline 不应再调用写入接口。

因此 Pipeline 要同时满足:

  • **预览结果**是一张(或一组)带好 gdim_id、且包含**前端写库所需业务列**的表,通常来自 MergeGdimTables.OutputTables;不必为了预览而读取或返回 GDIM 模板中的全部字段(见下文「预览字段与墓碑行」)。

  • 保存进 .pipe 的图里不能有 GdimTableWriter。平台恢复时会把所有模块 auto_run 设为 True,不能靠 auto_run=False 挡住写入。

推荐拓扑

主表场景的典型数据流:

GdimTableReader (源表)
     │ OutputTable
     ▼
RenameTableColumns / 其它整形模块   (把源表对齐到目标表名、列名)
     │
     ├──────────────────────────────┐
     ▼                              ▼
MergeGdimTables.InputTables    GdimTableReader (目标表, keep_gdim_id=True)
                                     │ OutputTables
                                     ▼
                               MergeGdimTables.InputGdimTables
                                     │
                                     ▼
                               MergeGdimTables.OutputTables  ← 预览端口
                                     │
                       (仅本地写回测试时,save_pipeline 之后再接)
                                     ▼
                               GdimTableWriter.InputData

要点:

  • 目标表读取必须 keep_gdim_id=True,这样合并结果才能区分插入 / 更新。

  • MergeGdimTables 以 InputGdimTables 侧读入的列结构 为输出 schema,并补齐 gdim_id;how、join_key 按业务选择(1:1 更新常用 overwrite + 业务键)。

  • return_results_config **必须**指向预览表端口,例如 {"MergeGdimTables": "OutputTables"}。 GdimTableWriter 没有面向用户的输出端口,不能当作预览结果。

  • 源表或目标表可能为空时,把对应 GdimTableReader.empty_error_type 设为 "create_empty_table",避免预览直接失败。

预览字段与墓碑行(前端写入 vs GdimTableWriter)

自定义数据更新(本章) 由 GDIM **前端**在用户确认后写库,Pipeline 只负责返回预览表:

  • **只需返回前端更新所需的字段**(以及区分插入 / 更新所需的 gdim_id)。GdimTableReader.table_fields 可以只列出本次要改的业务列,MergeGdimTables 预览结果也只需携带这些列。

  • 墓碑行如何删除由前端控制。 例如 MergeGdimTables 在 1:N overwrite 等场景下会输出「保留 gdim_id、预览表内业务列均为空」的行;用户确认后,前端会按平台规则处理删除(例如预览行在更新后整行业务字段为空时删除对应 GDIM 记录)。Pipeline **不必**为上传预览而读取 GDIM 模板的全部列。

仅在 Pipeline 内通过 ``GdimTableWriter`` 直接写库、且要靠墓碑行触发 ``delete_table_data`` 删整行时,才需要关心写入载荷是否覆盖 GDIM 模板中的**全部业务字段**(完整 schema 墓碑行、delete_tombstone_rows 等)。那属于 modules.writers 模块帮助 中「立即写回 / SDK Writer」的约定,**不是**本章「前端确认后写入」的默认要求。

本地在 save_pipeline() 之后临时接 GdimTableWriter 做写回测试时,若测试方式与线上一致(只写预览里的部分列),同样按前端契约理解删除行为即可;若希望 Writer 在本地走 delete_table_data 删整行,再按 modules.writers 模块帮助 准备完整 schema 载荷。

保存的 .pipe 不能连接 GdimTableWriter

在 build_pipeline() 里不要实例化、也不要连接 GdimTableWriter。 save_pipeline() 完成后再按需临时接上 writer 做本地写回测试。

这样上传到 GDIM 的 .pipe 只有「读 → 合并 → 返回预览表」,平台预览时不可能误写库。

不能把 writer 留在图里再设 auto_run=False。GDIM 加载 .pipe 时会把 所有模块的 auto_run 都改回 True, 预览运行就会写库。

两种场景对比:

写法

保存的 .pipe

适用

save_pipeline() 前不连接 writer

图中无 GdimTableWriter

「自定义数据更新配置」;GDIM 预览,前端确认后写入

普通立即写回

writer 默认 auto_run=True

数据处理应用、导入脚本等,运行即写库;**不是**本章功能

本地用 pipeline.run() 取预览表时,必须传入 return_results=pipeline.return_results_config。 默认收集的是末端模块端口;若之后接了没有输出端口的 writer,get_result("MergeGdimTables", "OutputTables") 会得到 None,即使合并已经成功。

主表完整示例

下面把标准地层表的若干字段按 layer_number 同步到岩土参数建议值表: 建议值表里已有、但标准地层表没有的层号予以保留。保存的 .pipe 不连接 writer; 本地若要真正写回,在 save_pipeline() 之后再临时接线。

from pathlib import Path

from gdisdk.connectors import log_in
from gdisdk.modules import (
    GdimTableReader,
    GdimTableWriter,
    MergeGdimTables,
    RenameTableColumns,
)
from gdisdk.pipeline import PipeLine

SOURCE_TABLE = "standard_layer_table"
TARGET_TABLE = "proposed_geotechnical_parameters_table"
TARGET_TABLE_TITLE = "岩土参数建议值表"
SYNC_FIELDS = [
    "layer_number",
    "material_name",
    "compactness",
    "plastic_state",
    "weathering_degree",
]

_SCRIPT_DIR = Path(__file__).resolve().parent
WORKSPACE_DIR = _SCRIPT_DIR / "workspace"


def build_pipeline() -> PipeLine:
    pipeline = PipeLine(
        app_name="geoParamsPropose1",
        app_title="同步标准地层到岩土参数建议值表",
        description="按 layer_number 把标准地层字段更新或插入岩土参数建议值表",
        workspace=WORKSPACE_DIR,
    )
    pipeline.update_gdim_state(token=log_in(), proj_id="你的GDIM项目ID")

    read_standard = GdimTableReader("ReadStandardLayer")
    read_standard.table_fields = {SOURCE_TABLE: SYNC_FIELDS}
    read_standard.output_table_name = SOURCE_TABLE
    read_standard.empty_error_type = "create_empty_table"

    read_proposed = GdimTableReader("ReadProposedParams")
    read_proposed.table_fields = [TARGET_TABLE]
    read_proposed.keep_gdim_id = True
    read_proposed.empty_error_type = "create_empty_table"

    rename_source = RenameTableColumns("RenameSourceTable")
    rename_source.table_name = TARGET_TABLE
    rename_source.table_title = TARGET_TABLE_TITLE

    merge_tables = MergeGdimTables("MergeGdimTables")
    merge_tables.how = "overwrite"
    merge_tables.join_key = "layer_number"
    merge_tables.auto_map_columns = False

    links = (
        read_standard.OutputTable >> rename_source.InputTable
        | rename_source.OutputTable >> merge_tables.InputTables
        | read_proposed.OutputTables >> merge_tables.InputGdimTables
    )
    pipeline.add_links(links)

    pipeline.return_results_config = {"MergeGdimTables": "OutputTables"}
    pipeline.save_pipeline(f"{pipeline.workspace}/{pipeline.app_name}.pipe")
    return pipeline


if __name__ == "__main__":
    pipeline = build_pipeline()
    result = pipeline.run(
        verbose=True,
        run_trace_file="run_trace.json",
        return_results=pipeline.return_results_config,
    )
    print(result.get_result("MergeGdimTables", "OutputTables"))

    # 仅本地写回测试:save_pipeline() 之后再接线,不要把这段写进要上传的 .pipe
    # write_proposed = GdimTableWriter("WriteProposedParams")
    # write_proposed.table_names = TARGET_TABLE
    # merge_tables = pipeline.get_module("MergeGdimTables")
    # pipeline.add_links(merge_tables.OutputTables >> write_proposed.InputData)
    # pipeline.run(return_results=pipeline.return_results_config)

子表还需要 auto_bind="pk_field"

若更新的是数字模型 子表,并且希望 GDIM 用「更新范围」下拉框注入 主表主键列表 (例如只更新若干钻孔下的地层行),在上述预览约定之外,再给读取模块加上:

pipeline.add_attribute(
    attr_name="primary_key_value_filter",
    module_name="ReadTable",
    param_name="primary_key_value_filter",
    attr_title="钻孔编号",
    auto_bind="pk_field",
)

这只是额外的前端注入,**不能代替**「预览端口 + 保存的 .pipe 不写库」。

  • 运行时值始终是 list[主键数据类型]``(例如 ``["ZK1"] 或 []),不要按标量处理。

  • 主表上的自定义更新会忽略该机制,属性按普通控件渲染。

  • 完整约定见 用户指南 (User Guide) 中「子表按主键更新(pk_field)」一节。

常见错误

  • 预览端口指错。 return_results_config 指向 GdimTableWriter,或设为 True 把所有末端输出都丢给前端。应指向合并后的预览表。

  • 上传的 .pipe 会写库。 save_pipeline() 前连接了 GdimTableWriter。平台恢复时会把所有模块 auto_run 设为 True,即使用户本地写成了 False,预览也会写库。

  • 把本地写回测试写进 .pipe 。 writer 只能出现在 save_pipeline() 之后的测试块里。

  • 指望 auto_run=False 挡住写入。 这对上传后的 GDIM 预览无效。

  • 本地 get_result 为 None 。 run() 未传 return_results=pipeline.return_results_config。

  • 更新变成插入。 目标表读取未设 keep_gdim_id=True,或合并前丢掉了 gdim_id。

  • 子表更新了整张表。 漏了 auto_bind="pk_field",或绑定参数只接受单个主键而不是列表。

  • 把本章约定用到普通写回应用。 立即写库的 Pipeline 应保持 GdimTableWriter.auto_run=True。

  • 误以为预览必须带 GDIM 模板全部列。 上传给「自定义数据更新」的 Pipeline 只需返回前端要改的字;完整 schema 墓碑行只在 Writer 直接写库且要 SDK 删整行 时才需要,见上文「预览字段与墓碑行」。

与其它文档的关系