検索・バルク・ビュー・添付 仕様書

全文検索・一括操作・保存済みフィルター・ファイル添付

ステータス: Draft / 作成日: 2026-05-27 PR #7 — 依存: コア、ファイル添付は Drive機能 も必要


1. 全文検索

データモデル

tasks テーブルに tsvector カラムを追加(Generated Column)。

前提: PostgreSQL 標準の tsvector は日本語辞書を内包しない。pg_bigm 拡張(バイグラム分割)または PGroonga をサーバーに導入した上で使用する。本仕様では pg_bigm を採用し、辞書名は pg_catalog.simple ではなく public.pg_bigm を使用する(§7 未決事項も参照)。

-- pg_bigm 拡張を有効化(スーパーユーザー権限が必要)
CREATE EXTENSION IF NOT EXISTS pg_bigm;

ALTER TABLE tasks
    ADD COLUMN search_vector tsvector
    GENERATED ALWAYS AS (
        to_tsvector('pg_catalog.simple',
            coalesce(title, '') || ' ' || coalesce(description, ''))
    ) STORED;

CREATE INDEX idx_tasks_search ON tasks USING GIN(search_vector gin_bigm_ops);

コメント全文も検索する場合は task_comments.body を別途検索してタスク ID でマージする。

API

GET /tasks/search?q=OAuth&limit=20&offset=0

レスポンス(スコア降順):

{
  "tasks": [
    {
      "id": "uuid", "seq_id": 42,
      "title": "OAuth 対応を実装する",
      "highlight": "…<em>OAuth</em> Apps を使って…",
      "score": 0.92
    }
  ],
  "total": 5
}

PostgreSQL の ts_headline で検索語をハイライト表示。'japanese' 辞書は pg_bigm 等の拡張が必要な場合は未決事項(後述)。


2. バルク操作

API

POST /tasks/bulk

リクエスト:

{
  "task_ids": ["uuid1", "uuid2", "uuid3"],
  "operations": [
    { "type": "set_status", "status_id": "uuid" },
    { "type": "add_label", "label_id": "uuid" },
    { "type": "set_sprint", "sprint_id": "uuid" }
  ]
}

対応する type:

type パラメータ
set_status status_id
set_priority priority
add_label label_id
remove_label label_id
set_assignee user_id, role
remove_assignee user_id
set_sprint sprint_id(null で解除)
set_milestone milestone_id(null で解除)
archive
unarchive
delete — (テナントオーナーのみ)

レスポンス:

{
  "updated": 3,
  "failed": []
}

3. 保存済みビュー

データモデル

pub struct SavedView {
    pub id: Uuid,
    pub project_id: Uuid,
    pub created_by: Uuid,
    pub name: String,
    pub is_shared: bool,
    pub filters: Value,    // JSON
    pub sort: Value,
    pub view_type: String, // board | list | table
    pub created_at: DateTimeUtc,
}
カラム 制約
id UUID PK
project_id UUID NOT NULL, FK→projects CASCADE
created_by UUID NOT NULL, FK→users
name VARCHAR(100) NOT NULL
is_shared BOOLEAN NOT NULL DEFAULT false
filters JSONB NOT NULL DEFAULT ''
sort JSONB NOT NULL DEFAULT ''
view_type VARCHAR NOT NULL DEFAULT 'list'
created_at TIMESTAMPTZ NOT NULL DEFAULT now()

filters JSONB スキーマ:

{
  "status_ids": ["uuid"],
  "priority": ["high", "critical"],
  "assignee_ids": ["uuid"],
  "label_ids": ["uuid"],
  "sprint_id": "uuid",
  "milestone_id": "uuid",
  "deadline_before": "2026-07-01",
  "is_archived": false,
  "has_no_assignee": false
}

マイグレーション

CREATE TABLE saved_views (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
    created_by UUID NOT NULL REFERENCES users(id),
    name VARCHAR(100) NOT NULL,
    is_shared BOOLEAN NOT NULL DEFAULT false,
    filters JSONB NOT NULL DEFAULT '{}',
    sort JSONB NOT NULL DEFAULT '{}',
    view_type VARCHAR NOT NULL DEFAULT 'list',
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

API

メソッド パス 説明
GET /views 一覧(自分のビュー + 共有ビュー)
POST /views 作成
PUT /views/{id} 更新(作成者 or テナントオーナー)
DELETE /views/{id} 削除

GET /tasks?view_id=uuid を渡すと保存済みフィルターを適用。


4. ファイル添付

Drive の drive_files をタスクへ紐付ける中間テーブル。

データモデル

カラム 制約
task_id UUID NOT NULL, FK→tasks CASCADE
drive_file_id UUID NOT NULL, FK→drive_files CASCADE
attached_at TIMESTAMPTZ NOT NULL DEFAULT now()
PRIMARY KEY(task_id, drive_file_id)

マイグレーション

CREATE TABLE task_attachments (
    task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
    drive_file_id UUID NOT NULL REFERENCES drive_files(id) ON DELETE CASCADE,
    attached_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (task_id, drive_file_id)
);

API

メソッド パス 説明
GET /tasks/{id}/attachments 添付ファイル一覧(メタデータ + プレビュー URL)
POST /tasks/{id}/attachments Drive ファイルを紐付け
DELETE /tasks/{id}/attachments/{file_id} 紐付け解除(Drive 本体は残る)

POST リクエスト(先に Drive API でアップロードして drive_file_id を取得する):

{ "drive_file_id": "uuid" }

レスポンスには MIME タイプ・コンテンツ URL を含める。フロントエンドが MIME タイプを見て表示を出し分ける。

プレビュー生成の方針:

ファイル種別 Phase A Phase B  
画像(image/ コンテンツ URL をそのまま返す(ブラウザが表示) サムネイル生成(image crate でリサイズ)
動画(video/) MIME タイプのみ返す(フロントはファイルアイコン表示) ffprobe + ffmpeg でサムネイル抽出(バックグラウンドジョブ)
その他 MIME タイプのみ  

動画サムネイルをまともに生成するには ffprobe / ffmpeg のサーバーへのインストールが必要。Phase A では動画は再生リンクのみ提供し、サムネイル生成は Phase B の追加タスクとして扱う。


5. マイタスク API

プロジェクト横断で自分担当のタスクを返す。

GET /v1/users/me/tasks?status=open&priority=high&limit=50

レスポンス: タスク一覧(project フィールドを含む)


6. フロントエンド(Phase B)

検索 UI

グローバルサーチ(Cmd+K / Ctrl+K)でタスクを横断検索。ハイライト付きで表示。

バルク操作 UI

タスク一覧でチェックボックスを選択 → ツールバーが出現 → ステータス・ラベル等を一括変更。

保存済みビュー UI

左サイドバーに一覧表示。現在のフィルター条件から「ビューとして保存」ができる。

添付プレビュー

MIME タイプ 表示
image/* サムネイル + クリックでフルサイズモーダル
video/* インラインプレーヤー
application/pdf ページプレビュー
その他 ファイル名 + ダウンロードリンク

コンポーネント

コンポーネント ファイル
GlobalSearch components/layout/GlobalSearch.vue
BulkActionBar components/tasks/BulkActionBar.vue
SavedViewSidebar components/tasks/SavedViewSidebar.vue
AttachmentList components/tasks/AttachmentList.vue
AttachmentPreview components/tasks/AttachmentPreview.vue
MyTasksPage pages/me/tasks/+Page.vue

7. 未決事項

項目 内容
日本語全文検索エンジン 本仕様では pg_bigm を採用(バイグラム分割。サーバーへの拡張インストールが必要)。将来的に Meilisearch/Typesense への移行も検討可