# 機能設計書 34-フィールドクエリ

## 概要

特定コレクションの特定フィールドを対象としたクエリ検索機能について記述する。Relationウィジェット等でフィールド値に基づいた候補検索を行う際に使用される。

### 本機能の処理概要

フィールドクエリ機能は、特定のコレクション内の特定フィールドに対して検索を行い、マッチした生データ（エントリではなくヒットオブジェクト）を返却するRedux Thunkアクションである。エントリ全文検索（searchEntries）とは異なり、フィールド単位の絞り込み検索を行う。

**業務上の目的・背景**：Relationウィジェットなどで他のコレクションのエントリを参照する際、ユーザーが入力した検索語に基づいてマッチする候補を動的に検索・表示する必要がある。フィールドクエリはこの要件を満たす特化型の検索機能である。

**機能の利用シーン**：エントリ編集画面でRelationウィジェットを操作する際、検索フィールドにテキストを入力すると、指定されたコレクションの指定フィールドに対して検索が実行され、候補一覧が表示される。

**主要な処理内容**：
1. クエリ識別子の生成（コレクション名+フィールド名+検索語+ファイル+制限数）
2. リクエストキューの確認（同一クエリの重複防止、10秒キャッシュ）
3. 統合プロバイダまたはバックエンドでのクエリ実行
4. 結果のnamespace付きReduxストア格納

**関連システム・外部連携**：統合検索プロバイダ（Algolia等）が設定されている場合はそちらを使用。

**権限による制御**：認証済みユーザーのみクエリ可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | エントリ編集画面 | 参照画面 | Relationウィジェット等でのフィールドクエリによる候補検索 |

## 機能種別

検索処理 / データ取得

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| namespace | string | Yes | クエリ結果の名前空間（ウィジェットごとの識別子） | なし |
| collectionName | string | Yes | 検索対象コレクション名 | なし |
| searchFields | string[] | Yes | 検索対象フィールド名の配列 | なし |
| searchTerm | string | Yes | 検索語 | なし |
| file | string | No | ファイルベースコレクションのファイル名 | なし |
| limit | number | No | 結果の最大件数 | なし |

### 入力データソース

- Relationウィジェット等のUI入力
- Reduxストア（state.collections, state.integrations, state.search.requests）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| namespace | string | 結果の名前空間 |
| hits | EntryValue[] | クエリにマッチしたヒットの配列 |

### 出力先

- Reduxストア（QUERY_SUCCESS / QUERY_FAILURE アクション経由）

## 処理フロー

### 処理シーケンス

```
1. query アクションの発行
   └─ namespace, collectionName, searchFields, searchTerm, file, limit を受け取る
2. clearRequests の発行
   └─ 期限切れリクエストのクリア
3. クエリ識別子の生成
   └─ `${collectionName}-${searchFields.join()}-${searchTerm}-${file}-${limit}`
4. リクエストキューの確認
   └─ 同一識別子のリクエストが存在すればそのPromiseを再利用
5. 検索の実行
   ├─ 統合プロバイダが存在する場合
   │   └─ integrationProvider.searchBy(searchFields, collectionName, searchTerm)
   └─ 統合プロバイダが存在しない場合
       └─ backend.query(collection, searchFields, searchTerm, file, limit)
6. QUERY_REQUEST アクション発行（searchTerm + リクエスト情報）
   └─ リクエスト情報: id, expires(10秒後), queryResponse
7. 結果の処理
   ├─ 成功: QUERY_SUCCESS（namespace, hits）
   └─ 失敗: QUERY_FAILURE（error）
```

### フローチャート

```mermaid
flowchart TD
    A[query呼び出し] --> B[clearRequests発行]
    B --> C[クエリ識別子生成]
    C --> D{キューに同一クエリ?}
    D -->|Yes| E[キューのPromise再利用]
    D -->|No| F{統合プロバイダ?}
    F -->|Yes| G[integrationProvider.searchBy]
    F -->|No| H[backend.query]
    G --> I[QUERY_REQUEST発行]
    H --> I
    E --> I
    I --> J{成功?}
    J -->|Yes| K[QUERY_SUCCESS]
    J -->|No| L[QUERY_FAILURE]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | クエリキャッシュ | 同一クエリ識別子のリクエストが10秒以内に存在する場合、そのPromiseを再利用する | state.search.requestsに同一idが存在する場合 |
| BR-34-02 | 統合プロバイダのフィールドプレフィックス | 統合プロバイダ使用時、searchFieldsの各フィールドに'data.'プレフィックスを追加 | 統合プロバイダ使用時 |
| BR-34-03 | namespace管理 | クエリ結果はnamespace単位で管理され、同一namespaceの結果は上書きされる | 常時 |

### 計算ロジック

クエリ識別子: `${collectionName}-${searchFields.join()}-${searchTerm}-${file}-${limit}`
クエリ有効期限: `new Date(new Date().getTime() + 10 * 1000)` （10秒後）

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| query | Reduxストア (search.requests) | UPDATE | リクエストキューの更新 |
| querySuccess | Reduxストア (search.queries) | UPDATE | namespace付きクエリ結果の格納 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| QUERY_FAILURE | クエリエラー | バックエンドまたは統合プロバイダのクエリが失敗した場合 | エラーオブジェクトをReduxストアに格納 |

### リトライ仕様

自動リトライなし。10秒のキャッシュ期限が切れた後に再度クエリ可能。

## トランザクション仕様

トランザクション管理なし（読み取り専用処理）

## パフォーマンス要件

- 10秒間のリクエストキャッシュにより重複クエリを防止
- clearRequestsで期限切れリクエストをクリア

## セキュリティ考慮事項

- バックエンドAPIへのアクセスには認証トークンが必要

## 備考

- QueryResponse型はhits: EntryValue[]とquery: stringを含む
- clearRequests()アクションでリクエストキューをクリアできる

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | search.ts | `packages/decap-cms-core/src/actions/search.ts` | QueryResponse型（70-73行目）: hits配列とquery文字列。QueryRequest型（7行目import）: Reducerで定義されるリクエストキュー型 |

**読解のコツ**: queryアクションはnamespace付きで結果を管理する点がsearchEntriesとの大きな違い。各Relationウィジェットインスタンスが固有のnamespaceを持つ。

#### Step 2: query関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | search.ts | `packages/decap-cms-core/src/actions/search.ts` | query関数（158-210行目）がエントリーポイント |

**主要処理フロー**:
1. **158-165行目**: query関数定義。namespace, collectionName, searchFields, searchTerm, file, limitを受け取る
2. **174行目**: clearRequests発行で期限切れリクエストをクリア
3. **176行目**: クエリ識別子の生成
4. **178行目**: リクエストキューで同一識別子を検索
5. **180-188行目**: キューにあればPromise再利用、なければ統合/バックエンドでクエリ
6. **190-201行目**: QUERY_REQUEST発行。新規リクエストの場合はキャッシュ情報付き
7. **203-208行目**: 結果のdispatch（querySuccess/queryFailure）

### プログラム呼び出し階層図

```
Relationウィジェット
    │
    ├─ dispatch(query(namespace, collectionName, searchFields, searchTerm, file, limit))
    │      ├─ dispatch(clearRequests())
    │      ├─ queryIdentifier生成
    │      ├─ state.search.requests.find() [キャッシュ確認]
    │      │      ├─ [キャッシュあり] queuedQueryPromise.queryResponse
    │      │      ├─ [統合あり] getIntegrationProvider().searchBy()
    │      │      └─ [バックエンド] backend.query()
    │      ├─ dispatch(querying(searchTerm, request))
    │      ├─ dispatch(querySuccess(namespace, hits))
    │      └─ dispatch(queryFailure(error))
    │
    └─ dispatch(clearRequests())
```

### データフロー図

```
[入力]                    [処理]                         [出力]

Relationウィジェット   ─▶ query()                     ─▶ Reduxストア更新
  namespace               ├─ clearRequests()               search.requests[]
  collectionName          ├─ キャッシュ確認                  search.queries[namespace]
  searchFields            ├─ プロバイダ選択                    .hits[]
  searchTerm              │   ├─ integration.searchBy()
  file                    │   └─ backend.query()
  limit                   └─ 結果dispatch
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| search.ts | `packages/decap-cms-core/src/actions/search.ts` | ソース | query関数および関連アクションクリエイタ |
| reducers/search.ts | `packages/decap-cms-core/src/reducers/search.ts` | ソース | クエリ結果・リクエストキューのReducer |
| integrations/index.ts | `packages/decap-cms-core/src/integrations/index.ts` | ソース | 統合プロバイダのsearchByメソッド |
| backend.ts | `packages/decap-cms-core/src/backend.ts` | ソース | バックエンドのqueryメソッド |
