---
document_type: "architecture_design"
service: "MOMENTRY_CORE"
title: "Momentry Core 分片資料庫結構規範 (Chunking Schema Spec) (v1.1)"
date: "2026-04-21"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
  - "schema"
  - "momentry"
  - "core"
  - "分片資料庫結構規範"
ai_query_hints:
  - "查詢 Momentry Core 分片資料庫結構規範 (Chunking Schema Spec) (v1.1) 的內容"
  - "Momentry Core 分片資料庫結構規範 (Chunking Schema Spec) (v1.1) 的主要目的是什麼？"
  - "如何操作或實施 Momentry Core 分片資料庫結構規範 (Chunking Schema Spec) (v1.1)？"
---

# Momentry Core 分片資料庫結構規範 (Chunking Schema Spec) (v1.1)

| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-04-21 |
| 最後更新 | 2026-04-22 |
| 文件版本 | V1.1 |
| **狀態** | **設計文檔（與實際實現有差異）** |

---

## 版本歷史

| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.1 | 2026-04-22 | 術語標準化：更新為實際實現的術語 | OpenCode | OpenCode / deepseek-v3.2 |
| V1.0 | 2026-04-21 | 定義符合 Chunking Rule 的完整資料庫結構與欄位對齊 | OpenCode | OpenCode / Qwen3.6-Plus |

---

## ⚠️ 重要說明：術語標準化

本文檔已根據實際 Rust 代碼實現更新術語。**核心原則**：當設計與實現出現矛盾時，以實際的 Rust 代碼實現為最高權威。

### 術語對照表
| 設計概念 | 設計值 | 實現值 | 實現狀態 |
|----------|--------|--------|----------|
| 時間基準分片 | `time` | `TimeBased` | ✅ 已實現 |
| 句子級分片 | `sentence` | `Sentence` | ✅ 已實現 |
| 場景級分片 | `scene` | `Cut` | ⚠️ 部分實現 |
| 視覺物件級分片 | `visual` | (未實現) | ❌ 未實現 |
| 摘要級分片 | `summary` | `Story` | ⚠️ 概念調整 |
| 軌跡追蹤分片 | (未定義) | `Trace` | ✅ 已實現 |

**參考文件**：[TERMINOLOGY_MAPPING.md](../../TERMINOLOGY_MAPPING.md)

---

## 0. 設計原則

本規範確保所有資料庫表嚴格遵循 **Chunking Architecture** 定義的通用結構：

1. **時間權威 (Frame-Based)**：所有時間相關欄位以 `frame` 為核心，`timestamp` 為計算參考。
2. **內容與元數據分離 (Content vs Metadata)**：`content` 用於全文檢索與向量嵌入，`metadata` (JSONB) 儲存關聯物件、Speaker、Faces 等結構化數據。
3. **路由清晰 (Rule Routing)**：每個 Rule 對應獨立的資料表，透過 `chunk_type` 欄位輔助識別。

---

## 1. 通用基礎欄位 (Common Base Columns)

以下欄位為**所有 Rule 表**的標準配置：

| 欄位 | 類型 | 約束 | 說明 |
|:---|:---|:---|:---|
| `id` | UUID | PK | Chunk 唯一標識符 |
| `asset_uuid` | UUID | FK, Not Null | 所屬影片資產 UUID |
| `chunk_type` | VARCHAR(20) | Not Null | 分片類型標識 (`TimeBased`, `Sentence`, `Cut`, `Trace`, `Story`) |
| `start_frame` | INT | Not Null | **起始幀** (時間基準) |
| `end_frame` | INT | Not Null | **結束幀** (時間基準) |
| `fps` | DOUBLE PRECISION | Not Null | 幀率 (用於換算秒數) |
| `content` | TEXT | Not Null | 檢索主體內容 |
| `created_at` | TIMESTAMPTZ | Default Now | 建立時間 |

---

## 2. Rule 1 結構：句子級 (Sentence)

對應 `CHUNK_RULE_1_SENTENCE.md`，用於 ASR 語句聚合。

```sql
CREATE TABLE IF NOT EXISTS chunks_rule1 (
    -- 通用基礎
    id UUID PRIMARY KEY,
    asset_uuid UUID NOT NULL REFERENCES assets(id),
    chunk_type VARCHAR(20) DEFAULT 'Sentence' NOT NULL,
    start_frame INT NOT NULL,
    end_frame INT NOT NULL,
    fps DOUBLE PRECISION NOT NULL,
    content TEXT NOT NULL, -- ASR 文本

    -- 關聯元數據
    speaker_id VARCHAR(50),            -- ASRX 說話者
    face_ids JSONB,                    -- 區間內出現的 Face ID 陣列
    face_confidence_map JSONB,         -- 對應的臉部信心值 (可選)

    -- 索引優化
    search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
    embedding vector(768)              -- nomic-embed-text-v2-moe
);

-- 索引
CREATE INDEX idx_r1_asset ON chunks_rule1(asset_uuid);
CREATE INDEX idx_r1_speaker ON chunks_rule1(speaker_id);
CREATE INDEX idx_r1_search ON chunks_rule1 USING gin(search_vector);
CREATE INDEX idx_r1_faces ON chunks_rule1 USING gin(face_ids);
```

---

## 3. Rule 2 結構：畫面級 (Visual) ⚠️ 未實現

**注意**: Rule 2 在實際代碼中尚未實現。以下為設計概念。

對應 `CHUNK_RULE_2_VISUAL.md`，用於 YOLO/Face 幀聚合。

```sql
CREATE TABLE IF NOT EXISTS chunks_rule2 (
    -- 通用基礎
    id UUID PRIMARY KEY,
    asset_uuid UUID NOT NULL REFERENCES assets(id),
    chunk_type VARCHAR(20) DEFAULT 'Visual' NOT NULL, -- ⚠️ 設計值，未實現
    start_frame INT NOT NULL,
    end_frame INT NOT NULL,
    fps DOUBLE PRECISION NOT NULL,
    content TEXT NOT NULL, -- 物件標籤串 (e.g., "car, person")

    -- 關聯元數據
    frame_objects JSONB,               -- 原始 YOLO 物件陣列 (含 BBox, Confidence)
    face_ids JSONB,                    -- 區間內出現的 Face ID 陣列
    speaker_id VARCHAR(50),            -- 當前說話者 (若無則為 Null)

    -- 索引優化
    search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
    embedding vector(768)
);

-- 索引
CREATE INDEX idx_r2_asset ON chunks_rule2(asset_uuid);
CREATE INDEX idx_r2_objects ON chunks_rule2 USING gin(frame_objects);
CREATE INDEX idx_r2_faces ON chunks_rule2 USING gin(face_ids);
```

---

## 4. Rule 3 結構：場景級 (Parent Scene)

對應 `CHUNK_RULE_3_SCENE.md`，作為 Parent Chunk，聚合多個 Rule 1/2。

```sql
CREATE TABLE IF NOT EXISTS chunks_rule3 (
    -- 通用基礎
    id UUID PRIMARY KEY,
    asset_uuid UUID NOT NULL REFERENCES assets(id),
    chunk_type VARCHAR(20) DEFAULT 'Cut' NOT NULL,
    start_frame INT NOT NULL,
    end_frame INT NOT NULL,
    fps DOUBLE PRECISION NOT NULL,
    content TEXT NOT NULL, -- 場景摘要 (Scene Summary)

    -- 關聯元數據 (聚合自子 Chunk)
    faces JSONB,                     -- 場景內所有不重複 Face IDs
    speakers JSONB,                  -- 場景內所有不重複 Speaker IDs
    objects JSONB,                   -- 場景內重要物件統計

    -- 索引優化
    search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
    embedding vector(768)
);

-- 索引
CREATE INDEX idx_r3_asset ON chunks_rule3(asset_uuid);
CREATE INDEX idx_r3_search ON chunks_rule3 USING gin(search_vector);
CREATE INDEX idx_r3_faces ON chunks_rule3 USING gin(faces);
```

---

## 5. Rule 4 結構：敘事分析級 (Story 5W1H) ⚠️ 概念調整

**注意**: Rule 4 在實際代碼中實現為 `Story` 分片，而非設計中的 `summary`。

對應 `CHUNK_RULE_4_SUMMARY.md`，LLM 分析產出的頂層 Chunk。

```sql
CREATE TABLE IF NOT EXISTS chunks_rule4 (
    -- 通用基礎
    id UUID PRIMARY KEY,
    asset_uuid UUID NOT NULL REFERENCES assets(id),
    chunk_type VARCHAR(20) DEFAULT 'Story' NOT NULL, -- 🔄 實際實現值
    start_frame INT NOT NULL,
    end_frame INT NOT NULL,
    fps DOUBLE PRECISION NOT NULL,
    content TEXT NOT NULL, -- LLM 生成的流暢劇情摘要

    -- 結構化分析
    analysis_5w1h JSONB NOT NULL,      -- 完整的 5W1H JSON 結構
    rule3_chunk_ids UUID[],            -- 組成此摘要的 Rule 3 ID 列表

    -- 關聯元數據
    faces JSONB,                       -- 區塊內人物
    objects JSONB,                     -- 區塊內物件

    -- 索引優化
    search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
    embedding vector(768)
);

-- 索引
CREATE INDEX idx_r4_asset ON chunks_rule4(asset_uuid);
CREATE INDEX idx_r4_5w1h_who ON chunks_rule4 USING gin((analysis_5w1h->'who'));
CREATE INDEX idx_r4_5w1h_what ON chunks_rule4 USING gin((analysis_5w1h->'what'));
```

---

## 6. 父子關聯 (Parent-Child Mapping)

為了支援規則間的聚合（如 Rule 3 聚合 Rule 1/2），我們需要一個映射表或外鍵。

### 方案 A：子表增加 Parent 欄位 (推薦)

在 `chunks_rule1` 與 `chunks_rule2` 增加欄位指向 Rule 3 或 Rule 4。

```sql
ALTER TABLE chunks_rule1 ADD COLUMN parent_rule3_id UUID REFERENCES chunks_rule3(id);
ALTER TABLE chunks_rule2 ADD COLUMN parent_rule3_id UUID REFERENCES chunks_rule3(id);
ALTER TABLE chunks_rule1 ADD COLUMN parent_rule4_id UUID REFERENCES chunks_rule4(id);
ALTER TABLE chunks_rule2 ADD COLUMN parent_rule4_id UUID REFERENCES chunks_rule4(id);
ALTER TABLE chunks_rule3 ADD COLUMN parent_rule4_id UUID REFERENCES chunks_rule4(id);
```

### 方案 B：獨立映射表 (更靈活)

```sql
CREATE TABLE chunk_relations (
    parent_id UUID NOT NULL,
    child_id UUID NOT NULL,
    relation_type VARCHAR(20), -- 'contains_sentence', 'contains_visual', 'aggregated_into_summary'
    PRIMARY KEY (parent_id, child_id)
);
```

---

## 7. 時間換算函數 (Helper)

資料庫內建輔助函數，確保秒數計算的絕對一致性。

```sql
CREATE OR REPLACE FUNCTION get_chunk_time(chunk_record ANYELEMENT)
RETURNS TABLE(start_sec DOUBLE PRECISION, end_sec DOUBLE PRECISION) AS $$
BEGIN
    RETURN QUERY SELECT 
        chunk_record.start_frame::double precision / chunk_record.fps,
        chunk_record.end_frame::double precision / chunk_record.fps;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
```

此設計確保了每一張表都完美對應 Chunking Architecture 的定義。
