データベース設計仕様書
Dexie.jsによるIndexedDBのスキーマ定義とデータ構造
データベース設計仕様書 (04_database_schema.md)
1. 概要
本アプリケーションのデータ永続化層は、クライアントサイドのIndexedDBをDexie.jsライブラリを介して利用しています。これにより、型安全なデータベースアクセスと効率的なクエリ、スキーマのバージョン管理を実現しています。
- データベース名:
blue-period-db - 主要ライブラリ: Dexie.js
- 実装ファイル:
next-app/src/lib/db.ts - 型定義ファイル:
next-app/src/lib/types.ts - 設計思想:
- データの自己所有: PublishなどのオンラインWebサービス以外の全てのプロジェクトデータはクライアントサイドに保存され、ユーザーデータのプライバシーと所有権を最優先します。このドキュメントで記述されているスキーマは、このクライアントサイドデータベースのものです。
- オンラインWebサービス: Supabaseによって実装されるWebサービス(Publish・Explore等)に公開する際、既存の公開枠に対する更新操作はサービス層(
publishProject)で所有権(user_idの一致)が厳密に検証されます。これにより、他者の公開作品を誤って上書きするリスクを排除しています。詳細は01_architecture.mdの「データ永続化」セクションを参照してください。 - スキーマバージョン管理: スキーマはDexieのバージョン管理機能を通じて段階的に更新され、現在は バージョン 24 です。
2. テーブル一覧
このデータベースは、以下のテーブルで構成されています。
| テーブル名 | 主な役割 | 主キーの型 |
|---|---|---|
projects | プロジェクト全体のメタデータと実データを格納 | UUID (string) |
chatPrompts | AIチャット設定用のプロンプトカードを管理 | UUID (string) |
promptAssets | 再利用可能なプロンプトアセットを管理 | UUID (string) |
characters | AIチャットのキャラクター(ペルソナ)を管理 | UUID (string) |
chatSessions | チャットのセッション情報を管理 | UUID (string) |
chatMessages | 個別のチャットメッセージを格納 | UUID (string) |
localSnapshots | 手動操作前のDBスナップショットを保持 | Auto-increment (number) |
vectorIndex | Oramaベクトルインデックスの永続化 | UUID (string) |
sessionArtifacts | エージェントセッションの資料(Artifacts)管理 | UUID (string) |
stats | 日次の執筆・読書統計データを格納 | YYYY-MM-DD (string) |
globalStore | アプリ全体の状態や永続的な小規模データ | string (key) |
3. テーブルスキーマ詳細
3.1. projects
プロジェクトの核心的なデータを格納するメインテーブルです。
-
目的: ユーザーが作成した各小説プロジェクトのメタデータ(タイトル、作者名など)と、プロットや原稿といった全てのコンテンツを保持します。
-
スキーマ定義:
&id, publishId, title, author, updatedAt&id: 主キー。UUID v4形式の文字列。publishId: インデックス付き。公開先スロットID。title: インデックス付き。プロジェクトのタイトル。author: インデックス付き。作者名。updatedAt: インデックス付き。最終更新日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | プロジェクトの一意なID (UUID) |
publishId | string (optional) | 公開先スロットID (UUID) |
title | string | プロジェクトのタイトル |
author | string (optional) | 著者名 |
imageUrl | string (optional) | 画像URL |
coverImage | `Blob | null` (optional) |
createdAt | Date | 作成日時 |
updatedAt | Date | 最終更新日時 |
content | ProjectContent | プロジェクトの本体データ。structuredPlot、manuscripts、writingPromptsが含まれる。 |
- データモデル (
ProjectRecord):interface ProjectRecord { id: string; // Primary Key (UUID) publishId?: string | null; // 公開先スロットID title: string; author?: string; imageUrl?: string; coverImage?: Blob | null; createdAt: Date; updatedAt: Date; content: ProjectContent; // ネストされたプロジェクト実体 revision?: number; // 保存操作の順序を識別するためのリビジョン番号 isSample?: boolean; // サンプルプロジェクトの場合true } interface ProjectContent { structuredPlot: StructuredPlot; manuscripts: Manuscript; writingPrompts: PromptCard[]; // 執筆プロンプト(スキーマバージョン12で統合) plotTranslations?: { // プロット要素の多言語翻訳(スキーマバージョン15付近で追加) [lang: string]: { [id: string]: PlotTranslation; } }; } interface PlotTranslation { title?: string; purpose?: string; content?: string; }
3.2. writingPrompts (※スキーマバージョン12で削除)
重要: 執筆プロンプトは、スキーマバージョン12で独立テーブルから削除され、プロジェクトの content.writingPrompts に統合されました。
-
以前の目的: 特定のプロジェクトに紐付く、再利用可能なプロンプト群を管理していました。
-
現在の状態: プロジェクトの
ProjectContentオブジェクト内のwritingPromptsフィールドに統合されています。 -
データモデル (
PromptCard):interface PromptCard { id: string; role: 'system' | 'user' | 'assistant'; content: string; enabled: boolean; cardType: 'text' | 'api_connector' | 'project_ref'; dynamicConfig?: DynamicConfig; order: number; projectId?: string; // プロジェクトとの紐付け updatedAt: Date; } -
プロジェクト構造 (
ProjectContent):interface ProjectContent { structuredPlot: StructuredPlot; manuscripts: Manuscript; writingPrompts: PromptCard[]; // 執筆プロンプトはここに統合 }
3.3. chatPrompts
AIチャット機能全体に影響を与えるシステムプロンプトを管理します。
-
目的:
writingPromptsとは異なり、特定のプロジェクトに依存しないグローバルなチャット設定用プロンプトを保持します。 -
スキーマ定義:
&id&id: 主キー。UUID v4形式の文字列。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | プロンプトカードの一意なID |
role | PromptCardRole | `'system' |
content | string | プロンプトの内容 |
enabled | boolean | このプロンプトが有効かどうか |
cardType | string | `'text' |
dynamicConfig | object | 動的設定(API URL、プロジェクトIDなど) |
order | number | カードの並び順 |
updatedAt | Date (optional) | 最終更新日時 |
- データモデル (
PromptCard): 上記writingPromptsと同様。
3.4. characters
AIチャットで対話相手となるキャラクター(ペルソナ)の情報を管理します。
-
目的: チャット相手のAIに特定の役割や口調を与えるための設定を保持します。
-
スキーマ定義:
&id, name, updatedAt&id: 主キー。UUID v4形式の文字列。name: インデックス付き。キャラクター名。updatedAt: インデックス付き。最終更新日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | キャラクターの一意なID (UUID) |
name | string | キャラクター名 |
prompt | string | キャラクターのペルソナを定義するプロンプト |
avatarUrl | string | アバター画像のBase64エンコードデータ |
createdAt | Date | 作成日時 |
updatedAt | Date | 最終更新日時(クラウド同期の差分検知に使用) |
- データモデル (
Character):interface Character { id: string; name: string; prompt: string; // このキャラクターの役割を定義するシステムプロンプト avatarUrl: string; // Base64 encoded image data createdAt: Date; updatedAt: Date; }
3.5. chatSessions
個々のチャットセッションのメタデータを管理します。
-
目的: 過去のチャット履歴をセッション単位で管理し、一覧表示や再開を可能にします。
-
スキーマ定義:
&id, character_id, updatedAt, createdAt&id: 主キー。UUID v4形式の文字列。character_id: インデックス付き。charactersテーブルへの外部キー。updatedAt: インデックス付き。セッションの最終更新日時。createdAt: インデックス付き。作成日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | セッションの一意なID (UUID) |
title | string | セッションのタイトル |
character_id | string | どのキャラクターとの会話かを示す外部キー (characters.id) |
createdAt | Date | 作成日時 |
updatedAt | Date | 最終更新日時 |
- データモデル (
ChatSession):interface ChatSession { id: string; title: string; character_id: string; // Foreign key to Character.id updatedAt: Date; createdAt: Date; }
3.6. chatMessages
各チャットセッションに含まれる個別のメッセージを時系列で格納します。
-
目的: ユーザーとAI間の対話履歴を完全に保存します。
-
スキーマ定義:
&id, session_id, createdAt, updatedAt&id: 主キー。UUID v4形式の文字列。session_id: インデックス付き。chatSessionsテーブルへの外部キー。createdAt: インデックス付き。メッセージの作成日時。updatedAt: インデックス付き。メッセージの最終更新日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | メッセージの一意なID (UUID) |
session_id | string | どのセッションに属するかを示す外部キー (chatSessions.id) |
author | `'user' | string` |
content | string | メッセージの内容 |
updatedAt | Date | 最終更新日時(同期および並び替え用) |
createdAt | Date | 作成日時 |
type | `'message' | 'separator'` (optional) |
metadata | ChatMessageMetadata (optional) | AIの応答に関するメタデータ |
- データモデル (
ChatMessage):interface ChatMessage { id: string; session_id: string; // Foreign key to ChatSession.id author: 'user' | string; // 'user' または Character.id content: string; createdAt: Date; updatedAt: Date; // スキーマバージョン21で追加 type?: 'message' | 'separator'; metadata?: ChatMessageMetadata; }
3.13. 索引に関する重要事項
IndexedDBにおいて日付フィールド(createdAt, updatedAt)を正しく索引するためには、標準の JavaScript Date オブジェクトである必要があります。Temporal APIのポリフィルオブジェクトや、単なるISO文字列をそのまま保存すると、索引から除外され、並び替えや検索が機能しなくなります。
これを防ぐため、データの保存および同期からの復元時には必ず @/lib/utils/date の ensureDate ユーティリティを使用します。
3.7. localSnapshots
危険な操作の直前に、ローカルデータベースのバックアップ(スナップショット)を保存します。
-
目的: 意図しないデータ消失や上書き事故が発生した際に、直前の状態に復旧できるようにします。
-
重要: データベースのエクスポート時(スナップショット作成、クラウド同期、手動エクスポート)には、再帰的な肥大化を防ぐため、この
localSnapshotsテーブル自体はエクスポート対象から除外されます。また、自動同期ループからも除外されます。 -
スキーマ定義:
++id, timestamp, reason++id: 主キー。timestamp: インデックス付き。作成日時のタイムスタンプ。reason: 作成理由(例: 'pre-sync')。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | number | スナップショットの一意なID |
timestamp | number | 作成日時のタイムスタンプ |
reason | string | 作成理由(例: 'pre-sync') |
data | Blob | データベース全体のBlobダンプ |
- データモデル (
SnapshotRecord):interface SnapshotRecord { id?: number; timestamp: number; reason: string; data: Blob; }
3.8. promptAssets
再利用可能なプロンプトテンプレート(アセット)を管理します。
-
目的: グローバルで再利用可能なプロンプトテンプレートを保持し、執筆やチャットの際にテンプレートとして利用できるようにします。タグ、説明、公開設定などのメタデータを含みます。
-
スキーマ定義:
&id, title, updatedAt&id: 主キー。UUID v4形式の文字列。title: インデックス付き。プロンプトアセットのタイトル。updatedAt: インデックス付き。最終更新日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | プロンプトアセットの一意なID |
title | string | プロンプトアセットのタイトル |
description | string (optional) | プロンプトアセットの説明 |
content | string | プロンプトの内容 |
assetType | string | `'text' |
dynamicConfig | object | 動的設定(アセット自体のデフォルト設定) |
tags | string[] (optional) | タグ一覧 |
enabled | boolean | このアセットが有効かどうか |
isPublic | boolean (optional) | 公開設定 |
createdAt | Date | 作成日時 |
updatedAt | Date | 最終更新日時 |
- データモデル (
PromptAsset):interface PromptAsset { id: string; title: string; description?: string; content: string; assetType: 'text' | 'api_connector' | 'project_ref'; dynamicConfig?: DynamicConfig; tags?: string[]; enabled: boolean; isPublic?: boolean; createdAt: Date; updatedAt: Date; }
3.9. vectorIndex
Oramaによる全文検索・ベクトル検索のためのインデックスデータを永続化するテーブルです。
-
目的: AIチャットの長期記憶(Long-Term Memory)機能において、ユーザーの過去の会話を検索可能にするためのベクトルインデックスを保存します。
-
スキーマ定義:
&id, updatedAt&id: 主キー。UUID v4形式の文字列。updatedAt: インデックス付き。最終更新日時。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | インデックスの一意なID |
data | unknown | シリアライズされたOramaインデックスデータ |
updatedAt | string | 最終更新日時(ISO文字列) |
modelId | string (optional) | 埋め込みモデルの識別子 |
3.9.1. ベクトル整合性とモデル管理
長期記憶のベクトル検索を正常に機能させるため、以下の整合性管理ルールが適用されます:
- 次元の一致: ベクトル次元数は埋め込みモデル(Embedding Model)に固有であり、異なるモデルのベクトルが同一のインデックスに混在することはできません。混在した場合、検索結果が破壊されるか、ランタイムエラーが発生します。
- モデル不一致の検出: 永続化された
modelIdがアプリの現在の設定値と異なる場合、システムはUI上で「再ベクタライズ」を促す警告を表示します。管理画面上では、不一致が発生していても旧モデルのインデックスからデータを一時的に読み出し、件数を表示することが可能です。 - 明示的操作: 記憶の取得、追加、更新、削除のすべての操作において、常に現在の
modelIdが明示的に渡され、そのモデルに対応する正しい Orama インスタンスが選択されます。 - 再ベクタライズ (実装済み): 過去のモデルで作成された記憶を現在の新モデルでも利用可能にするため、以下のプロセスを実行します:
- 旧モデルの
modelIdをキーに IndexedDB からシリアライズされたインデックスを取得。 - 旧 Orama インスタンスを展開し、全記憶ドキュメント(テキスト・メタデータ)を抽出。
- 現在(新)のモデルを使用して、テキストデータを再ベクトル化。
- 新しい Orama インデックスを生成し、同一のキー(
chat-memory-index)で IndexedDB を上書き保存。
- 旧モデルの
3.10. sessionArtifacts
エージェントセッションにおける一時的な資料(Artifacts)を管理するテーブルです。
-
目的: AIエージェントがWeb検索やプロジェクトデータ参照で得た情報を「机の上の資料」として保存し、後続の対話で再利用可能にします。
-
スキーマ定義:
&id, sessionId, isActive, createdAt&id: 主キー。UUID v4形式の文字列。sessionId: インデックス付き。紐付くチャットセッションID。isActive: インデックス付き。コンテキスト注入のON/OFF。createdAt: インデックス付き。作成日時(タイムスタンプ)。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
id | string | アーティファクトの一意なID |
sessionId | string | 紐付くチャットセッションのID |
type | string | アーティファクトの種類(`'web_search' |
title | string | 資料のタイトル |
content | string | 資料の内容(JSON文字列またはテキスト) |
isActive | boolean | コンテキスト注入が有効かどうか |
createdAt | number | 作成日時(タイムスタンプ) |
updatedAt | number | 更新日時(タイムスタンプ) |
3.11. stats
日次の執筆文字数や読書アクティビティを記録するための統計テーブルです。
-
目的: ユーザーの創作意欲を高めるため、日ごとの進捗を可視化します。
-
スキーマ定義:
datedate: 主キー。YYYY-MM-DD形式の文字列。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
date | string | 日付(YYYY-MM-DD) |
totalWordCount | number | その日の終了時点での全プロジェクト合計文字数 |
readPublishIds | string[] | その日に閲覧したプロジェクト/作品の公開スロットIDリスト |
updatedAt | number | 最終更新タイムスタンプ |
- データモデル (
DailyStats):interface DailyStats { date: string; totalWordCount: number; readPublishIds: string[]; updatedAt: number; }
3.12. globalStore
アプリケーション全体の状態管理や、特定のページの状態を永続化するための汎用的なキーバリューストアです。
-
目的: ページをリロードしても維持したい小さなデータ(ダッシュボードの目標値、スクラッチパッドの内容など)を保存します。
-
スキーマ定義:
keykey: 主キー。一意の識別子。
-
カラム詳細:
| カラム名 | 型 | 説明 |
|---|---|---|
key | string | キー(例: 'daily-word-goal', 'atelier-scratchpad') |
value | any | 保存される値 |
- データモデル:
interface GlobalStoreRecord { key: string; value: any; }
4. データポータビリティとエクスポートフォーマット
4.1. .bluememory (長期記憶エクスポート形式)
長期記憶 (Long-term Memory) のデータを特定の埋め込みモデルに依存しない形式で可搬させるためのフォーマットです。
- ファイル拡張子:
.bluememory - データ構造 (JSON):
version: フォーマットバージョン。memories: 各メッセージの内容、ロール、キャラクター背景、タイムスタンプを含むリスト。
- 設計思想:
- ベクトル非依存: ベクトル(Embedding)自体は保存せず、テキストとメタデータのみを保存します。これにより、インポート時に異なるモデル(例:
gte-multilingualからruri-v3)で再ベクトル化することが可能です。 - 選択的エクスポート: プロジェクト全体の情報を含まず、AIとの会話文脈(外部記憶)のみを抽出してポータブルにします。
- ベクトル非依存: ベクトル(Embedding)自体は保存せず、テキストとメタデータのみを保存します。これにより、インポート時に異なるモデル(例:
4.2. .blueperiod (プロジェクトバックアップ形式)
- 詳細:
doc/system/10_sync_architecture.mdを参照。 - 特徴: IndexedDB全体の完全に同一の環境を再現するためのフォーマットです。