业务故事站
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 月时窗子图:这个月图上发生了什么,切出来看
背景
韩老师要复盘 2026 年 8 月的图上动态。此前李诚图谱化导入了订单表,边都带 occurrence_time。他选定整个 8 月为时窗,调子图接口,把"8 月图上发生了什么"切成一张可渲染的子图。
传统做法对比
以前要看"某时间段的关系"得手工按时间筛几千条边,或者干脆看不出时间维度,7 月 8 月的边混在一起数不清;现在一个 POST 秒出,子图可单独渲染。
角色
韩老师(情报研判专家,需 admin 角色——时序接口仅管理员可执行;登录 admin / admin1,端口 18083)。
操作步骤
  1. 确认图数据边带 occurrence_time(图谱化导入配置 time_col 写入)
  2. POST /analysis/graph/temporal/subgraph(body {"from":"2026-08-01T00:00:00Z","to":"2026-08-31T23:59:59Z"})
  3. 查看命中边与两端节点
  4. 把子图单独渲染复盘
系统响应
返回时窗子图:
{
  "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 条边
背景
苏分析师想回答"8 月哪几天图上新关系最多"。她按天分桶统计新增边与新增节点,看关系爆发的时间点,再结合业务日历判断这天发生了什么。
传统做法对比
以前看节奏得把边按时间排序逐条数,几千条边数到眼花,或者干脆从事件表猜个大概;现在按桶自动聚合,柱状图一拉峰值在哪一眼可见。
角色
苏分析师(反洗钱分析师,需 admin 角色)。
操作步骤
  1. POST /analysis/graph/temporal/trend(body {"bucket":"day"})
  2. 换 bucket=week / month 看不同粒度
  3. 找 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 跳,跨时窗旧边不参与
背景
韩老师要回答"8 月 1 日到 8 月 10 日之间,客户 88 到订单 1500 隔几跳"。他指定时窗,让系统在时窗子图快照上求最短路径——跨时窗的旧边不参与,避免把 7 月的历史关系算进来。
传统做法对比
以前算"某时间段内的链路"得先把边按时间过滤出来再手工拼路径,或者干脆用全量图——把 7 月的旧边也算进去,结论失真;现在在时窗快照内寻路,跨时窗边自动排除。
角色
韩老师(情报研判专家,需 admin 角色)。
操作步骤
  1. POST /analysis/graph/temporal/path(body 含 from / to / window)
  2. 看 distance 与 path 序列
  3. 把时窗改小或改大对比可达性
系统响应
时窗内可达返回:
{
  "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。给图加上时间轴,演化才有据可查。