P4 Gotham
时序图分析:按时间切片的图上真相
图会随时间演化:订单在某个时间点生成、转账在某天发生。时序图分析按边的 occurrence_time 切时窗、按天/周/月桶看新增、在时窗快照内求路径,回答"某段时间图上发生了什么、节奏如何、链路多长"。看完这 3 个故事,你就能把"时间"这个维度加进图分析。
情报研判专家
反洗钱分析师
时窗过滤
时间桶
趋势统计
时窗路径
共 3 个故事
能 / 不能速览
✅ 这个主题能做
- TemporalSubgraph:边 Props["occurrence_time"] 按 [from,to] 闭区间时窗过滤,节点保留两端至少一条命中边者
- TemporalTrend:day / week / month 桶统计各桶新增边数与新增节点数(节点按首次出现计,跨桶去重)
- PathWithTime:先取时窗子图,在只读快照(temporalSubStore)上复用 ShortestPathWeighted(Dijkstra,weight 缺省 1)
- 时窗边界可省略:from / to 零值表示不设限(全时段)
- 时间格式兼容:RFC3339 优先,兼容 "2006-01-02 15:04:05" / "2006-01-02T15:04:05" / "2006-01-02"
- 输出确定性:子图按节点 / 边 ID 字典序,桶按时间升序;节点属性按脱敏策略输出
⛔ 这个主题做不了
- 依赖边带 occurrence_time:无该属性或解析失败的边不落入任何时窗(既有 seed / 手工边如此)
- from>to 或时间格式非法 → 422 VALIDATION_ERROR;bucket 非 day|week|month → 422
- 时窗内不可达(起点或终点不在时窗子图内)→ distance=-1 + hint,不是错误
- 三个端点全部挂在 admin 角色下,非 admin 一律 403
- 与 timeline 模块分工:temporal 看"图结构在时间上的演化",timeline 看"事件表的节奏"
适用角色
本主题面向两类角色:
- 情报研判专家:核心使用者,用时窗子图复盘"某段时间图上发生了什么",用趋势看关系爆发点。
- 反洗钱分析师:用时窗内最短路径还原"这段时间的资金 / 货流链路",排除跨时窗旧边干扰。
时序分析的三个端点与其它图分析接口一样挂在 admin 角色下;数据前提是边带 occurrence_time 属性(图谱化导入配置 time_col 写入,或手工补属性)。
能力速览(能做什么)
时窗子图
扫描全边按 occurrence_time 过滤,落在 [from,to] 闭区间的边 + 两端至少一条命中边的节点构成子图;时窗边界可省略。
时间桶趋势
按 day / week / month 桶统计各桶新增边数与新增节点数;节点按"时窗内最早命中边所在桶"计首次出现,跨桶不重复计数。
时窗内路径
先取时窗子图,在只读快照(写操作一律拒绝)上复用既有有权 Dijkstra 寻路,跨时窗的旧边不参与。
格式兼容
时间解析 RFC3339 优先,兼容常见日期格式;from / to 为空表示不设边界,时窗为闭区间含端点。
确定性输出
子图按节点 / 边 ID 字典序输出,桶按时间升序;节点属性按脱敏策略输出,避免敏感字段泄漏。
只读快照
temporalSubStore 实现 graph.GraphStore 但写操作一律返回只读错误,三个函数零改动 analysis 既有算法与 graph 包。
调整指南(怎么调整)
- 改时窗:from / to 决定切片范围,RFC3339 最稳;只给 from 看"此后",只给 to 看"此前"。
- 改桶粒度:bucket=day 看逐日、week 看周节奏、month 看月度爆发;非法值(如 hour)会 422。
- 改路径口径:时窗越窄路径越可能不可达,先放宽窗口确认连通性再收紧;边 weight 属性可表达权重,缺省按跳数。
- 改数据前提:无 occurrence_time 的边不落入任何时窗,先确认边是否经图谱化导入 time_col 写入或手工补了属性。
- 改分析组合:趋势峰值桶与业务事件(批量入库日、集中转账日)对照;把峰值日的子图单独拉出来复核。
做得好的场景
时序图分析把"时间"变成图上可切片的维度:
- 月度复盘:整个 8 月的图上动态一个 POST 切成子图,替代手工按时间筛几千条边。
- 关系爆发点:时间桶趋势直接标出哪几天新增节点 / 边最多,不用逐条数。
- 时段链路还原:时窗内最短路径排除跨时窗旧边,回答"这段时间两人隔几跳"。
- 与图谱化导入配套:图谱化导入把 time_col 写进边属性 occurrence_time,时序分析直接消费。
限制与不足
以下是明确的边界,使用前先知道:
- 数据前提硬:只认边属性 occurrence_time;无该属性或解析失败的边不落入任何时窗(既有 seed / 手工边如此)。
- 仅管理员可用:subgraph / trend / path 三个端点全部挂在 admin 角色下,非 admin 一律 403。
- 参数校验严格:from>to、时间格式非法、bucket 非 day|week|month、path 缺 from/to 都返回 422。
- 不可达语义:时窗内起点或终点不在子图内返回 distance=-1 + hint,调用时注意区分,不是错误。
- 与 timeline 分工:temporal 面向图边,timeline 面向事件表;两者数据源不同,互为补充不互替。
场景故事
故事 1
8 月时窗子图:这个月图上发生了什么,切出来看
场景:时窗子图
角色:情报研判专家
耗时:约 3 分钟
- 背景
- 韩老师要复盘 2026 年 8 月的图上动态。此前李诚图谱化导入了订单表,边都带 occurrence_time。他选定整个 8 月为时窗,调子图接口,把"8 月图上发生了什么"切成一张可渲染的子图。
- 传统做法对比
- 以前要看"某时间段的关系"得手工按时间筛几千条边,或者干脆看不出时间维度,7 月 8 月的边混在一起数不清;现在一个 POST 秒出,子图可单独渲染。
- 角色
- 韩老师(情报研判专家,需 admin 角色——时序接口仅管理员可执行;登录 admin / admin1,端口 18083)。
- 操作步骤
-
- 确认图数据边带 occurrence_time(图谱化导入配置 time_col 写入)
- POST /analysis/graph/temporal/subgraph(body {"from":"2026-08-01T00:00:00Z","to":"2026-08-31T23:59:59Z"})
- 查看命中边与两端节点
- 把子图单独渲染复盘
- 系统响应
- 返回时窗子图:
{
"nodes": [
{ "id": "orders:1001", "type": "orders", "label": "SO-1001" },
{ "id": "customers:88", "type": "customer", "label": "蓝港贸易" }
],
"edges": [
{ "id": "e_…", "source_id": "orders:1001", "target_id": "customers:88",
"type": "references_customers",
"props": { "occurrence_time": "2026-08-02T09:30:00Z" } }
]
}
节点属性按脱敏策略输出。
- 结果洞察
- 8 月时窗内命中的边(如 8-02 订单 1001 下单给客户 88)及其两端节点全部保留;发生在 7 月的订单边不落入时窗;无 occurrence_time 属性的边(如既有 seed 边)也不落入任何时窗。韩老师把 8 月子图单独渲染,一眼看清这个月的订单 / 客户骨架。
- 调整建议
- from / to 用 RFC3339 最稳;想全时段就省略 from / to(零值不设边界);时窗是闭区间含端点,边界当天的边会命中。
- 动手试一试
- 操作:对带 occurrence_time 的图 POST 8 月时窗子图。预期结果:返回时窗内边 + 两端节点;把 from 改到 9 月,预期返回空子图(该时段无边)。
- 限制提示
- 无 occurrence_time 的边不落入任何时窗(时序分析前先确认数据来源);from>to 或时间格式非法返回 422;仅 admin 可调。
故事 2
时间桶趋势:8 月 5 日那天,图上一下子多了 30 个节点 26 条边
场景:时间桶趋势
角色:反洗钱分析师
耗时:约 3 分钟
- 背景
- 苏分析师想回答"8 月哪几天图上新关系最多"。她按天分桶统计新增边与新增节点,看关系爆发的时间点,再结合业务日历判断这天发生了什么。
- 传统做法对比
- 以前看节奏得把边按时间排序逐条数,几千条边数到眼花,或者干脆从事件表猜个大概;现在按桶自动聚合,柱状图一拉峰值在哪一眼可见。
- 角色
- 苏分析师(反洗钱分析师,需 admin 角色)。
- 操作步骤
-
- POST /analysis/graph/temporal/trend(body {"bucket":"day"})
- 换 bucket=week / month 看不同粒度
- 找 edges 峰值桶,结合业务日历对照
- 系统响应
- 返回桶数组:
[
{ "time": "2026-08-01T00:00:00Z", "nodes": 12, "edges": 10 },
{ "time": "2026-08-02T00:00:00Z", "nodes": 8, "edges": 7 },
{ "time": "2026-08-05T00:00:00Z", "nodes": 30, "edges": 26 },
{ "time": "2026-08-06T00:00:00Z", "nodes": 2, "edges": 2 }
]
桶起点对齐(day=当日零点、week=周一零点、month=当月一日零点,UTC)。
- 结果洞察
- 8-05 出现明显峰值:新增 30 节点、26 条边——那天有一大批新订单 / 新客户集中入库。节点按"时窗内最早命中边所在桶"计首次出现,跨桶不重复计数,所以同一天首现的 30 个节点只算一次。苏分析师把峰值日的子图单独拉出来,核对那批入库的来源与映射批次。
- 调整建议
- 想看周节奏用 week、看月度爆发用 month;把趋势峰值与业务事件(批量入库日、集中转账日)对照;无命中边返回空数组(非错误)。
- 动手试一试
- 操作:POST /analysis/graph/temporal/trend,body 传 {"bucket":"day"}。预期结果:返回各桶 nodes / edges 统计,峰值桶一眼可见;传非法 bucket(如 "hour")预期 422 VALIDATION_ERROR。
- 限制提示
- 桶统计只依赖 occurrence_time 属性,无该属性的边不参与;bucket 仅 day|week|month;仅 admin 可调。
故事 3
时窗内最短路径:8 月上旬客户 88 到订单 1500 隔 2 跳,跨时窗旧边不参与
场景:时窗路径
角色:情报研判专家
耗时:约 4 分钟
- 背景
- 韩老师要回答"8 月 1 日到 8 月 10 日之间,客户 88 到订单 1500 隔几跳"。他指定时窗,让系统在时窗子图快照上求最短路径——跨时窗的旧边不参与,避免把 7 月的历史关系算进来。
- 传统做法对比
- 以前算"某时间段内的链路"得先把边按时间过滤出来再手工拼路径,或者干脆用全量图——把 7 月的旧边也算进去,结论失真;现在在时窗快照内寻路,跨时窗边自动排除。
- 角色
- 韩老师(情报研判专家,需 admin 角色)。
- 操作步骤
-
- POST /analysis/graph/temporal/path(body 含 from / to / window)
- 看 distance 与 path 序列
- 把时窗改小或改大对比可达性
- 系统响应
- 时窗内可达返回:
{
"path": ["customers:88", "orders:1001", "orders:1500"],
"nodes": [ … ],
"edges": [ … ],
"distance": 2
}
时窗内不可达返回(非错误):{
"distance": -1,
"hint": "起点或终点不在时窗子图内"
}
- 结果洞察
- 时窗子图先由 TemporalSubgraph 构造(只读快照,写操作一律拒绝),再在快照上复用有权 Dijkstra(边 weight 缺省 1)——8 月 1~10 日窗口内客户 88 经订单 1001 到订单 1500 共 2 跳。如果这条链路的某条边发生在 7 月,时窗内就查不到,distance=-1 正是"这段时间没有连线"的信号,值得追查。
- 调整建议
- 时窗越窄越可能不可达,先放宽窗口确认连通性再收紧;边 weight 属性可表达权重(如金额归一化倒数),缺省按跳数;把时窗内路径与全量路径对比,能看出"旧关系对时点判断的干扰"。
- 动手试一试
- 操作:对带 occurrence_time 的图 POST 时窗路径。预期结果:返回 distance 与 path 序列;把窗口改成订单发生前的时段,预期 distance=-1 并带 hint。
- 限制提示
- path 缺 from / to 返回 422;时窗内不可达返回 distance=-1 + hint,不是错误;时窗子图是只读快照,不会改动原图;仅 admin 可调。
常见问题
为什么我的时序分析返回空结果?
先确认边的属性里有没有 occurrence_time(RFC3339 或兼容格式)。无该属性或解析失败的边不落入任何时窗,返回空是正常语义。时序分析的数据前提是边带时间属性——图谱化导入配置 time_col 会写入,或手工补属性。
时窗边界含不含端点?
含。时窗是闭区间 [from,to],边界当天的边会命中;from / to 为零值时表示不设边界(不传 from 看"此前全部",不传 to 看"此后全部")。
时序图和 timeline 时间轴模块什么关系?
分工不同:timeline 面向 timeline_events 事件表做时间桶 / 预测 / 周期,看"事件节奏";时序图分析面向图边按 occurrence_time 做时窗过滤 / 趋势 / 路径,看"图结构在时间上的演化"。两者数据源不同,互为补充。
为什么我调用时序接口返回 403?
时序三个端点与分区接口一样挂在 admin 角色下,只允许管理员执行。请确认当前登录账号是 admin(登录 admin / admin1),或让管理员代为执行。
时窗内查不到路径是出错了吗?
不是。时窗内不可达(起点或终点不在时窗子图内)返回 distance=-1 并带 hint,不视为错误。业务上"这段时间没有连线"本身就是重要线索——要么链路发生在窗口外,要么确实没有关系。
主题小结
一句话:时序图分析把"时间"加进图分析——时窗子图回答"某段时间图上发生了什么"、时间桶趋势回答"节奏如何"、时窗内路径回答"这段时间链路多长"。数据前提是边带 occurrence_time(图谱化导入 time_col 写入或手工补属性)。记住边界:仅 admin 可用、无时间属性边不落任何时窗、from>to 与非法 bucket 返回 422、时窗内不可达返回 distance=-1。给图加上时间轴,演化才有据可查。