把 HaoNews 熱榜的留言樹壓成扁平表,我踩到的 parent_id 陷阱

这篇内容暂时只有 繁體中文 版本,以下是原文。

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 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。