Skip to content

Studio Fix: Face Group Name 讀寫不一致

Date: 2026-07-27 Author: Studio Team Status: ✅ 已修復並部署


問題描述

Studio 前端在 Face 頁面 rename group name 後,離開再回來時名稱未更新。但 People Search 可以查到新名稱。

根因

寫入和讀取走了不同的資料來源:

操作 API Endpoint 實際讀寫欄位
寫入 (rename) PUT /api/v1/trace-profile/group trace_profiles.name
讀取 (卡片顯示) GET /api/v1/file/:uuid/face-groups tkg_nodes.label

trace_profiles.name 和 tkg_nodes.label 是兩張獨立的表/欄位(見 19_people_api.md),rename 只更新了 trace_profiles.name,但卡片顯示讀的是 tkg_nodes.label。


修復內容

修改檔案

/Users/accusys/momentry_core/src/api/profile.rs — get_face_groups_handler (line 552-591)

改前 SQL

SELECT label, properties FROM tkg_nodes
WHERE file_uuid = $1 AND node_type = 'face_track'
ORDER BY (properties->>'trace_id')::int

改後 SQL

SELECT COALESCE(tp.name, tn.label) as name, tn.properties
FROM tkg_nodes tn
LEFT JOIN trace_profiles tp ON tp.file_uuid = tn.file_uuid
    AND tp.trace_id = (tn.properties->>'trace_id')::int
WHERE tn.file_uuid = $1 AND tn.node_type = 'face_track'
ORDER BY (tn.properties->>'trace_id')::int

Rust 程式碼變更

 pub async fn get_face_groups_handler(...) {
     let tkg_table = schema::table_name("tkg_nodes");
+    let tp_table = schema::table_name("trace_profiles");

     let rows: Vec<(String, serde_json::Value)> = sqlx::query_as(&format!(
-        "SELECT label, properties FROM {} \
-         WHERE file_uuid = $1 AND node_type = 'face_track' \
-         ORDER BY (properties->>'trace_id')::int",
-        tkg_table
+        "SELECT COALESCE(tp.name, tn.label) as name, tn.properties \
+         FROM {} tn \
+         LEFT JOIN {} tp ON tp.file_uuid = tn.file_uuid \
+             AND tp.trace_id = (tn.properties->>'trace_id')::int \
+         WHERE tn.file_uuid = $1 AND tn.node_type = 'face_track' \
+         ORDER BY (tn.properties->>'trace_id')::int",
+        tkg_table, tp_table
     ))
     ...
-    for (label, properties) in rows {
+    for (name, properties) in rows {
         ...
-        if label.starts_with("Face Trace ") || label.starts_with("Trace ") {
+        if name.starts_with("Face Trace ") || name.starts_with("Trace ") {
             unassigned.push(trace_id);
         } else {
-            groups.entry(label).or_default().push(trace_id);
+            groups.entry(name).or_default().push(trace_id);
         }
     }
 }

影響範圍

項目 影響
Face 頁面卡片顯示 ✅ 現在從 trace_profiles.name 讀取,rename 後立即生效
People Search 不受影響(本來就查 trace_profiles.name)
未命名 traces 不受影響(COALESCE fallback 到 tkg_nodes.label,如 Person_0)
Cluster Agent 初始化 不受影響(初始化時兩邊都寫入相同值)

注意事項(給 Core Team)

  1. 未來如果有需要同時更新 tkg_nodes.label 的情境(例如 graph 顯示需要),請注意 PUT /api/v1/trace-profile/group 目前只更新 trace_profiles.name
  2. 建議:如果 tkg_nodes.label 和 trace_profiles.name 應該保持一致,可以考慮:
  3. 方案 A(目前做法):讀取端用 COALESCE 優先取 trace_profiles.name
  4. 方案 B:寫入端同時更新兩個欄位(需要改 update_trace_profile_group_handler)
  5. merge_groups_handler 目前同時更新 trace_profiles.name 和 tkg_nodes.label(line 278-310),行為正確,不需要改

驗證

# 測試 face-groups endpoint 正確回傳 rename 後的名稱
curl -s "http://localhost:3002/api/v1/file/4655a0ab3c077e30c12b2298c2750650/face-groups" \
  -H "X-API-Key: <key>" | jq '.face_groups[] | {name, trace_count}'

# 預期輸出包含 "Susan"(已 rename 的 group)
# { "name": "Susan", "trace_count": 7 }