P4 Gotham
图谱化导入:让数据库表一行一节点直达图上
多源融合的"融合导入"先产候选、再靠解析作业上图;图谱化导入是 V5 新增的并行路径——database 源配好映射后,按主键 keyset 分页拉行、外键自动探测、一行一节点直接上图,融合导入后同一任务追加图谱化。看完这 4 个故事,你就能把一张订单表直接变成可做图分析的节点和边。
数据接入工程师
情报分析员
keyset 分页
外键探测
映射配置
行级导入
共 4 个故事
能 / 不能速览
✅ 这个主题能做
- database 源配置图谱映射(gf_graph_mappings,一源一映射)后,Ingest 同一任务内追加行级图谱化(进度 0.6~1.0)
- KeysetReader 键集分页:WHERE pk>last ORDER BY pk ASC LIMIT n 循环拉行,chunk_size 默认 1000,主键游标分页不丢行
- DetectForeignKeys 外键自动探测:PG/MySQL 读 referential_constraints,SQLite 读 PRAGMA foreign_key_list
- MapRowsToGraph 构造 GraphNode(ID=<type>:<pk>)+ 按 edge_rules 对外键列生成 GraphEdge(Props 含 occurrence_time)
- InferMapping 自动建议:读源表 schema + 探测外键,返回未保存建议与全列清单,前端确认后落库
- 图存储幂等:UpsertNode / UpsertEdge 幂等,重复执行同一次图谱化不产生重复节点与边
⛔ 这个主题做不了
- 仅 database 源可图谱化,文件源 / 手工源返回 GOTHAM_GRAPH_MAPPING_INVALID
- 仅 MySQL / PostgreSQL / SQLite 三连接器实现 KeysetReader,其余连接器类型断言失败
- 悬挂边跳过不报错(edges_skipped):跨表目标节点未导入时无法生成边
- 图谱化不参与清洗 / 质量评估;图上节点没有融合候选的 pending / resolved 状态语义
- 标识符白名单校验:表名 / 列名必须匹配 ^[A-Za-z_][A-Za-z0-9_]*$,中文或含空格列名会 422 报"非法的 SQL 标识符"
- 图谱化节点与解析作业产物并存不合并,归并语义由图存储 MergeNode 承担
适用角色
本主题面向两类角色:
- 数据接入工程师:核心使用者,配置图谱映射、跑自动建议、执行图谱化、排查悬挂边。
- 情报分析员:消费图谱化产出的图节点与边,把它作为图分析 / 时序分析的输入。
图谱化导入与融合导入(多源融合主题)共存:融合导入仍走"候选池 → 解析",图谱化导入是 database 源的直达路径,两条链路可以并存。
能力速览(能做什么)
Keyset 键集分页
可选接口 KeysetReader 按主键游标分页(WHERE pk>last ORDER BY pk ASC LIMIT n 循环),大表不一次性全量拉取,chunk_size 默认 1000。
外键自动探测
DetectForeignKeys 读数据库元数据:PG/MySQL 走 referential_constraints,SQLite 走 PRAGMA foreign_key_list,复合外键逐列返回。
映射配置
table_name / node_id_col / node_type / node_label_col / prop_cols / time_col / edge_rules / chunk_size / enabled,一源一映射。
行级上图
MapRowsToGraph 两遍扫描:Pass1 构造 GraphNode(ID=<type>:<pk>,Props 含行级 source 标注),Pass2 按 edge_rules 对外键列生成 GraphEdge(含 occurrence_time)。
自动建议
InferMapping 读 schema + 探测外键,主键 / 文本列 / 时间列启发式识别,FK 列生成 references_<to_table> 边规则建议,返回未保存建议。
融合追加
IngestAsync 落库后追加图谱化步骤(进度 0.6~1.0),include_ingest 默认 true;未配置 / 未启用映射时完整退化为既有行为。
调整指南(怎么调整)
- 改分页粒度:chunk_size 控制 keyset 每页行数(默认 1000),大表调大减少页数、求稳调小。
- 改节点:node_id_col 必填(主键列,节点 ID=<node_type>:<pk值>);node_label_col 可空,缺省用 pk 值当展示名。
- 改边:edge_rules 每条含 type + from_col,to_column / to_node_type 缺省经外键探测补全;想带金额等额外属性用 edge_props。
- 改时间维度:time_col 非空时写入边属性 occurrence_time(RFC3339),是时序图分析(temporal)的数据前提。
- 改执行范围:include_ingest=true 先融合导入再图谱化;已进过候选池的库可设 false 只做图谱化。
- 改补边顺序:先导被引用表(如 customers)再导主表(如 orders),可把悬挂边数降到最低。
做得好的场景
图谱化导入最擅长把"库里已有的关系型数据"一步变成图上可分析的节点和边:
- 订单表直接进图:5000 行订单按 keyset 分页拉取、一行一节点,毫秒级落图,替代专用导入脚本。
- 外键自动成边:customer_id 外键自动探测并生成 references 边,不用手写边规则。
- 与大表兼容:主键游标分页 + 图存储幂等,重复执行不重复、大表不内存爆掉。
- 与融合导入并存:同一任务内候选落库与图上节点/边双产出,融合链路语义不变。
限制与不足
以下是明确的边界,使用前先知道:
- 仅 database 源:文件源 / 手工源调用图谱化接口直接返回 GOTHAM_GRAPH_MAPPING_INVALID。
- 连接器能力约束:KeysetReader 仅 MySQL / PostgreSQL / SQLite 三实现,其他连接器类型断言失败。
- 悬挂边不报错:目标节点未导入时边按 edges_skipped 跳过,排查"边少了"先确认被引用表导入顺序。
- 不参与清洗 / 质量评估:图谱化走的是"行级直上"路径,清洗、质量评估仍是融合导入的职责。
- 标识符白名单:中文或含特殊字符的列名 / 表名会被拒绝(防注入刻意收紧),需改列名或走融合映射路径。
场景故事
故事 1
5000 行订单表 keyset 分页拉行,5 页落图一个不丢
场景:keyset 分页
角色:数据接入工程师
耗时:约 6 分钟
- 背景
- 李诚要把销售库的 orders 表(5000 行)直接进图。他先建好 database 数据源(SQLite 库,connector 工厂需显式授权 SQLITE),再进图谱映射页配置:table_name=orders、node_id_col=id、node_type=orders、chunk_size=1000,保存后点"执行图谱化",看 keyset 分页怎么逐页把 5000 行拉完。
- 传统做法对比
- 以前要么一次 SELECT * 全量拉进内存,几万行就吃力、再大直接内存爆掉;要么写自增 id 分页脚本,脚本没人维护就烂尾。现在主键游标分页,WHERE pk>last ORDER BY pk ASC LIMIT n 循环到空结果为止,天然适配大表。
- 角色
- 李诚(数据接入工程师,具备数据源与图谱映射配置权限;登录 admin / admin1,端口 18083)。
- 操作步骤
-
- 进入图谱映射页 /gotham/graph-mapping,选中 database 型数据源
- 配置映射:table_name=orders、node_id_col=id、node_type=orders、chunk_size=1000
- PUT /ingestion/sources/12/graph-mapping 保存映射
- POST /ingestion/sources/12/map-to-graph 提交图谱化任务
- 系统响应
- 任务创建返回 task 记录:
{
"id": "2f9a…",
"type": "gotham_graph_mapping",
"status": "queued",
"progress": 0,
"message": "",
"created_by": "admin"
}
任务完成后结果:{
"source_id": 12, "batch_id": 23, "table": "orders",
"nodes_imported": 5000, "edges_created": 0, "edges_skipped": 0,
"pages": 5, "started_at": "2026-08-29T10:12:00Z", "finished_at": "2026-08-29T10:12:06Z"
}
5000 行按每页 1000 分成 5 页(pages=5)拉取。
- 结果洞察
- 5000 行分 5 页全部导入,nodes_imported=5000 与源表行数一致,一个不丢;游标回填时主键值归一化(优先 int64、其次 float64)保证绑定参数类型稳定。再执行一次同任务,nodes_imported 仍 5000——图存储 UpsertNode 幂等,重复导入不产生重复节点。
- 调整建议
- chunk_size 调大减少页数(默认 1000)、调小更稳;node_id_col 必须选主键列,keyset 以它排序分页才稳定;标识符校验白名单要求列名只能是字母数字下划线。
- 动手试一试
- 操作:对 orders 表配置 node_type=orders、node_id_col=id 后执行图谱化。预期结果:任务结果 nodes_imported=5000、pages=5;再执行一次,nodes_imported 不变(幂等直观效果)。
- 限制提示
- 仅 database 源可图谱化;连接器须实现 KeysetReader(MySQL/PostgreSQL/SQLite);表名 / 列名必须匹配标识符白名单,中文列名直接 422 报"非法的 SQL 标识符"。
故事 2
外键自动探测:点一下"自动建议",customer_id 到 customers 的边规则自己出来了
场景:外键探测
角色:数据接入工程师
耗时:约 4 分钟
- 背景
- 李诚想把"订单 → 客户"的归属关系也一并上图上,但 orders 表结构他没背下来,外键定义更记不清。他点"自动建议",让系统读源表 schema、探测外键,把映射配置的建议直接回填到表单里。
- 传统做法对比
- 以前要打开数据库管理工具翻表结构、逐条查外键定义,再手写边规则;表一多、外键一多,漏一条就断一条链路,还得靠回忆核对。现在探测结果直接回填,漏配的边规则一眼可见。
- 角色
- 李诚(数据接入工程师,配置图谱映射与核对自动建议)。
- 操作步骤
-
- GET /ingestion/sources/12/mapping-suggest?table=orders
- 查看返回的 columns 全列清单、foreign_keys 探测结果、edge_rules 建议
- 确认回填到表单,勾选 prop_cols
- PUT /ingestion/sources/12/graph-mapping 保存
- 系统响应
- 自动建议返回未保存的映射建议:
{
"table_name": "orders",
"node_id_col": "id",
"node_type": "orders",
"node_label_col": "order_no",
"prop_cols": { "amount": "amount", "status": "status" },
"time_col": "created_at",
"edge_rules": [
{ "type": "references_customers", "from_col": "customer_id",
"to_table": "customers", "to_column": "id" }
],
"columns": ["id", "order_no", "customer_id", "amount", "status", "created_at"],
"foreign_keys": [
{ "from_column": "customer_id", "to_table": "customers", "to_column": "id" }
]
}
建议未落库,确认后才保存。
- 结果洞察
- SQLite 连接器用 PRAGMA foreign_key_list(orders) 探测出 customer_id→customers.id 外键,系统自动生成 references_customers 边规则;主键列、文本列、时间列启发式识别(created_at 被 looksLikeTimeCol 命中)一起填进建议。foreign_keys 数组与 edge_rules 一一对应,李诚核对一遍就确认保存。
- 调整建议
- 建议回填后检查 prop_cols 是否勾全;想写边属性 occurrence_time 就保留 time_col;目标表还没导的边规则可以先保存,执行时按悬挂边跳过,不影响主流程。
- 动手试一试
- 操作:对 orders 表跑自动建议(mapping-suggest?table=orders)。预期结果:返回全列 columns、外键 foreign_keys 1 条、edge_rules 建议 1 条(references_customers),确认保存后 GET graph-mapping 能看到已落库映射。
- 限制提示
- 自动建议只读不改数据;外键探测依赖数据库元数据(PG/MySQL 走 referential_constraints、SQLite 走 PRAGMA);无外键的表返回空 foreign_keys,需要手工配 edge_rules。
故事 3
一行一节点 + 外键生成边:orders:1001 连上了 customers:88
场景:行级映射
角色:数据接入工程师
耗时:约 5 分钟
- 背景
- 周警官要查"订单 → 客户"的图链路,前提是 orders 表每一行都变成图上节点、并按外键连到客户节点。李诚配置好完整映射(含 edge_rules)后执行图谱化,看 GraphEdge 是怎么按外键列生成的。
- 传统做法对比
- 以前接库表进图要么写专用导入脚本一边建节点一边建边,脚本一崩两头都丢;要么先落融合候选再跑解析作业,多一步还得等。现在映射配置 + 一键执行,一次任务双落库。
- 角色
- 李诚(数据接入工程师,执行图谱化并核对节点/边);周警官(情报分析员,消费图数据)。
- 操作步骤
-
- 确认映射含 prop_cols 与 edge_rules(references_customers)
- POST /ingestion/sources/12/map-to-graph(body 缺省 include_ingest=true)
- 轮询 GET /tasks/:id 查看任务进度
- 到图工作台展开 orders:1001 查看 references_customers 边
- 系统响应
- 任务结果返回:
{
"source_id": 12, "batch_id": 25, "table": "orders",
"nodes_imported": 5000, "edges_created": 4998, "edges_skipped": 2,
"pages": 5, "started_at": "2026-08-29T10:12:00Z", "finished_at": "2026-08-29T10:12:06Z"
}
图上边示例:{
"id": "e_…",
"source_id": "orders:1001",
"target_id": "customers:88",
"type": "references_customers",
"props": { "occurrence_time": "2026-08-02T09:30:00Z",
"confidence": 1.0, "fk_column": "customer_id" }
}
节点 ID 统一为 <node_type>:<pk值>。
- 结果洞察
- 两遍扫描保证了同表前后页互引的节点都存在:Pass1 建全部节点、Pass2 按 edge_rules 对外键列生成边。5000 节点、4998 条 references_customers 边一次完成,2 条边被跳过是因为对应客户在 customers 表里不存在(悬挂边)——跳边只计数不报错。
- 调整建议
- edge_rules 的 to_node_type 缺省取 to_table;to_column 缺省经外键探测补全;想带金额等额外属性用 edge_props;先导 customers 再导 orders 能把悬挂边降到零。
- 动手试一试
- 操作:配置含 edge_rules 的映射后执行图谱化。预期结果:edges_created≈4998、edges_skipped≈2;到图上 1 度展开 orders:1001 能看到 references_customers 边指向 customers:88。
- 限制提示
- 悬挂边只计数 edges_skipped 不中断任务;图存储幂等保证重复执行不重复建边;图谱化节点没有融合候选的 pending / resolved 状态,不参与实体解析状态流转。
故事 4
融合导入后追加图谱化:一个任务串完候选落库与图上建边,进度 0.6 到 1.0
场景:融合追加
角色:数据接入工程师
耗时:约 7 分钟
- 背景
- 李诚不想"先跑融合导入、再单独跑图谱化"两步操作,他直接用"执行图谱化"(include_ingest=true),让同一个任务先做融合导入(候选落库,进度 0~0.6)、再追加图谱化(图上建节点和边,进度 0.6~1.0)。这次他故意先不导 customers 表,看悬挂边怎么被记录。
- 传统做法对比
- 以前融合导入和图谱化是两套脚本两条流水线,跑完导入还要等图谱化排队,中间环节出错要两头排查;现在一个任务串完,进度一路可见,双落库一次完成。
- 角色
- 李诚(数据接入工程师,执行融合追加图谱化并排查悬挂边)。
- 操作步骤
-
- 确认数据源已配置图谱映射且 enabled=true
- POST /ingestion/sources/12/map-to-graph(body {"include_ingest": true})
- 轮询 GET /tasks/:id:0~0.6 融合导入、0.6~1.0 图谱化
- 查看任务最终结果与悬挂边计数
- 系统响应
- 任务运行中返回:
{
"id": "2f9a…", "type": "gotham_graph_mapping",
"status": "running", "progress": 0.8,
"message": "图谱化完成:节点 5000,边 4863(跳过 137,失败 0)"
}
最终结果:{
"source_id": 12, "batch_id": 28, "table": "orders",
"nodes_imported": 5000, "edges_created": 4863, "edges_skipped": 137,
"pages": 5, "started_at": "…", "finished_at": "…"
}
融合候选(fused_entities)与图上节点 / 边双落库。
- 结果洞察
- 同一任务内 5000 条融合候选落库、5000 个图上节点建完,但 137 条 references_customers 边被跳过——因为 customers 表还没图谱化导入,orders→customer 的目标节点不存在。把 customers 表先图谱化、再重跑 orders,edges_skipped 会归零。未配置 / 未启用映射时,Ingest 完整退化为既有行为,不报错。
- 调整建议
- 先导被引用表(customers)再导主表(orders)减少悬挂边;include_ingest=false 可跳过融合导入只做图谱化(数据已进过候选池时用);任务进度是总进度,图谱化阶段映射到 0.6~1.0。
- 动手试一试
- 操作:include_ingest=true 执行图谱化。预期结果:任务 progress 先到 0.35(融合导入完成)再推上 1.0;把 customers 表也图谱化后重跑 orders,edges_skipped 归零。
- 限制提示
- 融合导入阶段占 0~0.6、图谱化占 0.6~1.0;GET /tasks 与 GET /tasks/:id 已注册,可直接轮询任务进度;图谱化不参与清洗与质量评估。
常见问题
图谱化导入和融合导入有什么区别?
融合导入(多源融合主题)产出融合实体候选(fused_entities),真正上图要跑实体解析作业;图谱化导入是 V5 新增的并行路径,database 源配好映射后"一行一节点"直接上图并生成外键边,不用经过候选池与解析。两者共存:图谱化节点没有候选的 pending / resolved 状态,与解析产物并存不合并。
哪些数据源可以图谱化?
仅 database 型数据源,且连接器要实现 KeysetReader(当前 MySQL / PostgreSQL / SQLite 三实现)。文件源 / 手工源调用图谱化接口会返回 GOTHAM_GRAPH_MAPPING_INVALID。SQLite 连接器默认不在 allow-list,需要显式授权(AllowAdvancedTypes("SQLITE"))。
为什么我的边少了?
先看任务结果里的 edges_skipped:悬挂边(跨表目标节点未导入)只计数不报错。两遍扫描已尽量少悬挂,但被引用表(如 customers)没图谱化导入就无法生成边。先导被引用表再导主表即可补全。
映射的列名带中文为什么报错?
标识符有白名单校验(^[A-Za-z_][A-Za-z0-9_]*$),表名 / 列名含空格、连字符或中文会被拒绝并返回 422"非法的 SQL 标识符"。这是防 SQL 注入的刻意收紧,需要改列名或走融合映射路径。
图谱化导入会走清洗和质量评估吗?
不会。图谱化是"行级直上"路径,清洗规则与四维质量评估仍是融合导入的职责。如果需要清洗脏数据,先在融合导入侧配清洗规则,再叠加图谱化步骤。
主题小结
一句话:图谱化导入把"库里已有数据"一步变成图上可分析的节点和边——配置映射(node_id_col / node_type / prop_cols / time_col / edge_rules)→ 自动建议回填 → 执行图谱化,keyset 分页拉行、外键自动成边、图存储幂等防重复,融合导入后同一任务追加图谱化(进度 0.6~1.0)。记住边界:仅 database 源、仅三连接器支持、悬挂边跳过不报错、不参与清洗 / 质量、标识符白名单严格。