Meilisearch インデックススキーマ
Meilisearch の subtitles インデックスは 1 ドキュメント = 字幕の 1 セグメント として設計される。
スキーマレスだが searchableAttributes / filterableAttributes / sortableAttributes で宣言されたフィールドのみ検索・絞り込み・ソートの対象になる。
実装は apps/api/src/lib/meilisearch/subtitles-index/subtitle-document.ts を参照。VM / Docker / API キーの運用手順は Meilisearch セットアップ に分離している。
更新タイミングの詳細は Meilisearch 更新タイミング を参照。
ドキュメントフィールド
| フィールド | 型 | searchable | filterable | sortable | 供給元(Turso) | 更新タイミング概要 |
|---|---|---|---|---|---|---|
id | string | ❌ | ❌ | ❌ | 生成({videoId}_{segmentIndex}) | ドキュメント投入時(primary key) |
videoId | string | ❌ | ✅ | ❌ | videos.id | 同上 |
channelId | string | ❌ | ✅ | ❌ | videos.channel_id | 同上 |
title | string | ✅ | ❌ | ❌ | videos.title | 同上。オーナーによる動画タイトル変更は再インデックスまで追従しない |
text | string | ✅ | ❌ | ❌ | Turso には存在しない。字幕本文は Meilisearch が唯一の保存先 | 字幕取得フロー(subtitle-fetch-orchestrator)完了時(T8) |
startTime | number (sec) | ❌ | ✅ | ✅ | セグメントの開始秒 | 同上。sortable は「前後 5 秒コンテキスト」取得時の範囲クエリソートに使用 |
endTime | number (sec) | ❌ | ❌ | ❌ | セグメントの終了秒 | 同上 |
language | string | ❌ | ✅ | ❌ | セグメン トの言語コード | 同上 |
publishedAt | number (Unix 秒) | ❌ | ✅ | ✅ | videos.published_at(ISO → Unix 秒変換) | 同上。sortable は公開日ソートに使用 |
viewCount | number | null | ❌ | ✅ | ❌ | videos.view_count | 動画の初回取得時のみ。以降は更新されない(更新戦略は別 Issue) |
likeCount | number | null | ❌ | ✅ | ❌ | videos.like_count | 動画の初回取得時のみ。以降は更新されない |
categoryId | string | null | ❌ | ✅ | ❌ | videos.category_id | 動画の初回取得時 |
channelCountry | string | null | ❌ | ✅ | ❌ | channels.country | 字幕取得フロー時にチャンネルの最新 country を冗長保存。値が変動した場合は該当チャンネルの全ドキュメントを updateDocuments で追従更新(#305 以降) |
durationSeconds (※1) | number (sec) | ❌ | ❌ | ✅(#84 で追加予定) | videos.duration_seconds | 動画の初回取得時に確定(動画の長さは変動しないため以降の更新不要) |
subscriberCount (※1) | number | null | ❌ | ❌ | ✅(#84 で追加予定) | channels.subscriber_count | 字幕取得フロー時に channel の最新値を冗長保存。fetchChannelDiff の 24h refresh で値が変動した場合は該当チャンネルの全ドキュメントを updateDocuments(partial update)で追従更新 |
※1: #84 で新規追加予定のフィールド。本ドキュメントは実装前の設計状態を示す。
インデックス属性設定
Meilisearch 側の制約として filterableAttributes は 1 インデックスあたり 上限 10 個。現在 9 個で残り 1 枠。sortableAttributes には明確な上限なし。
| 属性 | 現在の値 | #84 完了後 |
|---|---|---|
searchableAttributes | ["text", "title"] | 変更なし |
filterableAttributes | ["channelId", "language", "videoId", "startTime", "publishedAt", "viewCount", "likeCount", "categoryId", "channelCountry"](9 / 10) | 変更なし(#84 の追加フィールドは sortable 限定) |
sortableAttributes | ["publishedAt", "startTime"] | ["publishedAt", "startTime", "durationSeconds", "subscriberCount"] |
冗長保存フィールドの同期戦略
text など一部を除くほぼ全フィールドは Turso の値を冗 長保存している。Turso 側が変動した際にどう同期するかは以下の通り。
| フィールド | 供給元の更新トリガー | ドキュメント同期の実装 |
|---|---|---|
channelCountry | channels.country が fetchChannelDiff(T7)で変動(#305) | 同チャンネルの全ドキュメントを updateDocuments で partial update |
subscriberCount(#84) | channels.subscriber_count が fetchChannelDiff で変動 | 同上(変動なしならスキップ) |
viewCount / likeCount | 動画単位の更新戦略が未実装 | #84 スコープ外。別 Issue で議論予定 |
updateDocuments は partial update(指定フィールドのみ更新、他フィールドは保持)なので、本文 text を誤って上書きしない設計。
関連資料
- Meilisearch 更新タイミング — 各フィールドがいつ書き込まれるか、Turso からの伝播経路
- Meilisearch セットアップ — VM / Docker / API キーの運用
- データ戦略 — ストレージ選定の背景