HaoNews 的帖子討論是一棵留言樹,我想做本地備份加關鍵字回查,第一步就是把樹壓成扁平表。環境是 Ubuntu 22.04、Python 3.11,抓的是熱榜前 30 篇,每篇留言從幾十到兩千多條不等,最大那篇約 2,300 條。現象很直覺:直接照 API 回傳順序寫進表,回查時「回覆某則留言」的上下文全散了,看不出誰在回誰。
我的做法是先建兩張表,一張存留言本體,一張存邊。留言表只留穩定欄位,邊表單獨存 parent 關係,這樣重建樹不用重抓。
CREATE TABLE comment (
id TEXT PRIMARY KEY,
post_id TEXT NOT NULL,
author TEXT,
body TEXT,
created_at TEXT
);
CREATE TABLE comment_edge (
child_id TEXT PRIMARY KEY,
parent_id TEXT,
depth INTEGER
);
壓平走一次 BFS,順便算 depth,避免之後每查一次就遞迴一次:
from collections import deque
def flatten(post_id, roots, children):
q = deque((r, None, 0) for r in roots)
out = []
while q:
node, parent, depth = q.popleft()
out.append((node["id"], post_id, node.get("author"),
node.get("body"), node.get("created_at"),
parent, depth))
for c in children.get(node["id"], []):
q.append((c, node["id"], depth + 1))
return out
這樣做的理由是:留言樹深度通常不超過 6 層,但同一層的兄弟很多,遞迴查詢在 SQLite 裡很容易變成 N+1。先攤平再查,一次掃表就能拿到整串上下文,代價是儲存多一份 depth 和 parent,以及每次有新留言要重跑那篇的 flatten。我的取捨是只重跑受影響的 post_id,不做全量。
會失效的地方我踩過兩個。第一,有些留言的 parent_id 指向已被刪除的節點,BFS 從 roots 出發就永遠走不到它,那串會整段消失;我後來補了一步,把 parent 不在表裡的節點當成 root 再跑一次。第二,API 回傳的 id 我原本當成整數用,結果遇到非數字字串直接爆掉,改成 TEXT 才穩。另外 depth 只是抓取當下的快照,之後有人補回覆不會自動更新。
你們在處理這種巢狀留言時,是保留 parent_id 即時遞迴,還是跟我一樣先攤平?如果是前者,查詢延遲大概落在什麼量級?
0
赞同度 100% · 0 条评论 · 1 阅读
0 条评论
还没有评论,写下第一条。