# 機能設計書 40-Attachmentプロセッサ

## 概要

本ドキュメントは、OpenSearchのAttachmentプロセッサ機能の設計を記述する。AttachmentプロセッサはApache Tikaを使用してバイナリファイル（PDF、Microsoft Office文書等）からテキスト、メタデータ、言語情報を抽出するインジェストプロセッサである。

### 本機能の処理概要

Attachmentプロセッサは、ドキュメントのBase64エンコードされたバイナリフィールドを読み取り、Apache Tikaライブラリを使用してテキストコンテンツ、タイトル、著者、キーワード、作成日、Content-Type、言語情報を抽出し、ターゲットフィールドに構造化データとして設定するプロセッサである。

**業務上の目的・背景**：PDF、Word、Excel、PowerPoint等のバイナリドキュメントの全文検索を実現するために、ファイルからテキストを自動抽出する。ドキュメント管理システムやファイルリポジトリの全文インデックス作成に利用される。

**機能の利用シーン**：インジェストパイプラインのprocessors配列内でattachmentプロセッサを定義し、Base64エンコードされた添付ファイルデータのフィールドを指定して利用する。

**主要な処理内容**：
1. ドキュメントからBase64エンコードされたバイナリフィールド値を取得
2. indexedCharsField（またはデフォルトindexedChars）で抽出文字数上限を決定
3. Apache Tika（TikaImpl.parse）でバイナリデータを解析
4. テキストコンテンツの抽出
5. メタデータ（タイトル、著者、作成日、キーワード、Content-Type）の抽出
6. OptimaizeLangDetectorによる言語検出
7. propertiesに基づいてターゲットフィールドに結果を設定

**関連システム・外部連携**：Apache Tikaライブラリ。OptimaizeLangDetector（言語検出）。セキュリティマネージャによる分離実行。

**権限による制御**：インジェストパイプラインの管理権限に従う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | インジェストパイプライン内で利用されるプロセッサ |

## 機能種別

データ連携（データ抽出・エンリッチメント）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| field | String | Yes | Base64エンコードされたバイナリデータのフィールド名 | 存在するフィールド |
| target_field | String | No | 出力先フィールド（デフォルト"attachment"） | - |
| properties | String[] | No | 抽出するプロパティ一覧 | サポートされるプロパティ |
| indexed_chars | Integer | No | 抽出する最大文字数（デフォルト100000） | 正の整数 |
| indexed_chars_field | String | No | ドキュメントから最大文字数を取得するフィールド名 | - |
| ignore_missing | Boolean | No | フィールド欠損時に無視するか（デフォルトfalse） | - |
| resource_name | String | No | リソース名（Content-Type検出のヒント） | - |

### 入力データソース

IngestDocumentのBase64エンコードされたバイナリフィールド。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| attachment.content | String | 抽出されたテキストコンテンツ |
| attachment.title | String | ドキュメントタイトル |
| attachment.author | String | 著者名 |
| attachment.date | String | 作成日 |
| attachment.keywords | String | キーワード |
| attachment.content_type | String | Content-Type |
| attachment.content_length | Long | コンテンツ長 |
| attachment.language | String | 検出された言語コード |

### 出力先

IngestDocumentのtarget_fieldにマップ形式で設定。

## 処理フロー

### 処理シーケンス

```
1. IngestDocumentからフィールド値を取得
   └─ getFieldValueAsBytes()でバイナリデータを取得
2. indexed_charsの決定
   └─ indexed_chars_fieldから取得、なければデフォルト値
3. Apache Tikaでバイナリデータを解析
   └─ TikaImpl.parse(input, metadata, indexedChars)
4. テキストコンテンツの抽出
   └─ parsedContentをtrimして設定
5. メタデータの抽出
   └─ TikaCoreProperties.CREATED, TITLE, Author等
6. 言語検出
   └─ OptimaizeLangDetector.detect(parsedContent)
7. Content-Type, Content-Length抽出
   └─ Metadata.CONTENT_TYPE, CONTENT_LENGTH
8. ターゲットフィールドに結果を設定
   └─ IngestDocument.setFieldValue(targetField, additionalFields)
```

### フローチャート

```mermaid
flowchart TD
    A[バイナリフィールド取得] --> B{値がnull?}
    B -->|Yes, ignoreMissing| C[スキップ]
    B -->|Yes, not ignoreMissing| D[例外スロー]
    B -->|No| E[indexed_chars決定]
    E --> F[TikaImpl.parse実行]
    F --> G{ZeroByteFileException?}
    G -->|Yes| H[空コンテンツとして処理]
    G -->|No| I[テキスト抽出成功]
    I --> J[propertiesに基づく抽出]
    H --> J
    J --> K[CONTENT: テキスト設定]
    K --> L[LANGUAGE: 言語検出]
    L --> M[DATE/TITLE/AUTHOR/KEYWORDS設定]
    M --> N[CONTENT_TYPE/CONTENT_LENGTH設定]
    N --> O[targetFieldに設定]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | デフォルト抽出文字数 | indexed_charsのデフォルトは100000文字 | indexed_chars未指定時 |
| BR-40-02 | ゼロバイトファイル | ZeroByteFileExceptionは無視して空コンテンツとして処理 | 空ファイル |
| BR-40-03 | 著者フィールド | "Author"が取得できない場合はTikaCoreProperties.CREATORをフォールバック | 著者抽出時 |
| BR-40-04 | キーワードフィールド | "Keywords"が取得できない場合はTikaCoreProperties.SUBJECTをフォールバック（EPUBs対応） | キーワード抽出時 |
| BR-40-05 | 言語検出 | OptimaizeLangDetectorでテキストコンテンツから言語を自動検出 | LANGUAGE property指定時 |
| BR-40-06 | セキュリティ分離 | TikaImplはセキュリティマネージャにより分離されたクラスローダーで実行 | 全解析処理 |

### 計算ロジック

- テキスト抽出: Apache TikaのAutoDetectParser使用
- 言語検出: OptimaizeLangDetectorによる統計的言語判定

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| テキスト抽出 | なし（インメモリ） | 変換 | バイナリデータからテキスト/メタデータを抽出 |

### テーブル別操作詳細

直接的なデータベース操作はない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 500 | illegal_argument_exception | フィールド値がnullでignore_missing=false | ignore_missing=trueに設定 |
| 500 | opensearch_parse_exception | バイナリデータの解析失敗 | サポートされるファイル形式を使用 |
| - | - | ZeroByteFileException（空ファイル） | 自動的に空コンテンツとして処理 |

### リトライ仕様

プロセッサエラーはインジェストパイプラインのon_failureハンドラで処理。

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

IngestDocument単位での処理。トランザクション制御は不要。

## パフォーマンス要件

- Apache Tikaの解析処理はファイルサイズに比例したコスト
- indexed_charsで抽出文字数を制限することでメモリ使用量とCPU使用量を制御
- 大量の添付ファイル処理時はバルクリクエストのスループットに影響

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

- TikaImplはセキュリティマネージャにより分離されたクラスローダーで実行され、Tikaの脆弱性によるシステムアクセスを防止
- バイナリファイルの解析には潜在的なセキュリティリスク（悪意のあるファイルによるDoS等）があるため、indexed_charsによる制限が重要
- プラグインとして提供されるためインストール時の権限確認が必要

## 備考

- ingest-attachmentプラグインとして提供
- IngestAttachmentPluginでプラグイン登録
- TikaImplはセキュリティ分離された専用クラスローダーでApache Tikaを実行
- サポートファイル形式：PDF, DOC/DOCX, XLS/XLSX, PPT/PPTX, HTML, RTF, TXT, EPUB等（Tika対応形式）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AttachmentProcessor.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java` | プロセッサ本体。Property列挙型でCONTENT, TITLE, AUTHOR, DATE, KEYWORDS, CONTENT_TYPE, CONTENT_LENGTH, LANGUAGEを定義 |

**読解のコツ**: AttachmentProcessor.Propertyは出力フィールドの種類を定義する列挙型。各Propertyはname()の小文字がフィールドキーとして使用される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | IngestAttachmentPlugin.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java` | プラグイン登録。getProcessors()でattachmentプロセッサファクトリを返却 |
| 2-2 | AttachmentProcessor.java (execute) | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java` | execute()メソッド |

**主要処理フロー**:
- **61行目**: AttachmentProcessorクラス定義。TYPE = "attachment"
- **65行目**: NUMBER_OF_CHARS_INDEXED = 100000（デフォルト）
- **67-72行目**: field, targetField, properties, indexedChars, ignoreMissing, indexedCharsFieldフィールド
- **98-129行目**: execute()メソッド。getFieldValueAsBytes()でバイナリ取得、TikaImpl.parse()でテキスト抽出
- **131-133行目**: CONTENT proeprty: parsedContent.trim()を設定
- **136-141行目**: LANGUAGE property: OptimaizeLangDetector.detect()で言語検出
- **144-148行目**: DATE property: TikaCoreProperties.CREATEDから取得
- **158-168行目**: AUTHOR property: "Author"→CREATORのフォールバック

#### Step 3: Apache Tika統合を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TikaImpl.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java` | Apache Tikaのセキュリティ分離実行。parse()メソッド |

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

```
IngestAttachmentPlugin
    |
    +-- getProcessors() -> {"attachment": AttachmentProcessor.Factory}
          |
          +-- Factory.create() -> AttachmentProcessor
                |
                +-- execute(IngestDocument)
                      |
                      +-- getFieldValueAsBytes(field)
                      |
                      +-- TikaImpl.parse(input, metadata, indexedChars)
                      |     +-- AutoDetectParser
                      |     +-- BodyContentHandler
                      |     +-- セキュリティ分離クラスローダー
                      |
                      +-- [CONTENT] parsedContent.trim()
                      +-- [LANGUAGE] OptimaizeLangDetector.detect()
                      +-- [DATE] TikaCoreProperties.CREATED
                      +-- [TITLE] TikaCoreProperties.TITLE
                      +-- [AUTHOR] "Author" / CREATOR
                      +-- [KEYWORDS] "Keywords" / SUBJECT
                      +-- [CONTENT_TYPE] Metadata.CONTENT_TYPE
                      +-- [CONTENT_LENGTH] Metadata.CONTENT_LENGTH
                      |
                      +-- IngestDocument.setFieldValue(targetField, additionalFields)
```

### データフロー図

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

IngestDocument       +--> Base64バイナリ取得
(field: "data")      |     getFieldValueAsBytes()
                     |
                     +--> indexed_chars決定
                     |     (indexedCharsFieldまたはデフォルト)
                     |
                     +--> TikaImpl.parse()
                     |     +-- AutoDetectParser
                     |     +-- テキスト抽出
                     |     +-- メタデータ抽出
                     |
                     +--> OptimaizeLangDetector
                     |     +-- 言語検出
                     |
                     +--> additionalFieldsマップ構築   +--> IngestDocument
                           (content, title, author,          (target_field: attachment)
                            date, keywords, language,
                            content_type, content_length)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AttachmentProcessor.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java` | ソース | Attachmentプロセッサ本体 |
| IngestAttachmentPlugin.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java` | ソース | プラグイン登録 |
| TikaImpl.java | `plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java` | ソース | Apache Tikaセキュリティ分離実行 |
