# 機能設計書 39-AQL（Actual Query Language）

## 概要

本ドキュメントは、データベースクエリ用の独自クエリ言語AQL（Actual Query Language）について記述する。

### 本機能の処理概要

**業務上の目的・背景**：Actualは内部でSQLiteデータベースを使用しているが、直接SQLを書くことはセキュリティ上のリスクや型安全性の問題がある。AQLは、型安全でセキュアなクエリ構築を可能にする独自のクエリ言語である。JSONライクな構文でクエリを定義し、内部でSQLにコンパイルして実行する。これにより、開発者は複雑なJOINや集計を安全に行うことができる。

**機能の利用シーン**：
- レポート機能でのデータ集計
- 取引検索・フィルタリング
- カスタムクエリの実行
- API経由でのデータ取得
- 分割取引のグループ化取得

**主要な処理内容**：
1. クエリ状態（QueryState）の構築
2. フィールド参照（$field）の解決
3. 関数呼び出し（$sum, $count等）のコンパイル
4. 条件演算子（$eq, $gte, $like等）のコンパイル
5. JOINパスの解決とテーブル結合
6. SQLの生成と実行
7. 結果の型変換と返却

**関連システム・外部連携**：SQLiteデータベース、スキーマ定義

**権限による制御**：予算ファイルへの読み取り権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 37 | 数式レポート画面 | 主機能 | カスタム数式によるレポート計算・表示 |

## 機能種別

データアクセス / クエリ言語 / コンパイラ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| query | Query \| QueryState | Yes | クエリオブジェクトまたはシリアライズされた状態 | 有効なテーブル名 |
| params | AqlQueryParams | No | 名前付きパラメータ | - |

### クエリ構文

#### 基本構文

```typescript
q('transactions')
  .filter({ category: 'cat-id' })
  .select(['id', 'date', 'amount'])
  .orderBy([{ date: 'desc' }])
  .limit(100)
```

#### フィールド参照

- `$field`: フィールド値を参照
- `$`: 暗黙のフィールド参照
- `account.name`: JOINパスによる関連テーブル参照

#### 関数

- `$sum`: 合計
- `$count`: カウント
- `$month`: 日付から月を抽出
- `$year`: 日付から年を抽出
- `$day`: 日付から日を抽出
- `$substr`: 部分文字列
- `$lower`: 小文字変換
- `$neg`: 符号反転
- `$abs`: 絶対値

#### 演算子

- `$eq`: 等価
- `$ne`: 非等価
- `$gt`, `$gte`: 大なり、以上
- `$lt`, `$lte`: 小なり、以下
- `$like`: パターンマッチ
- `$notlike`: パターン非マッチ
- `$oneof`: 配列内に存在
- `$regexp`: 正規表現マッチ

### 入力データソース

クエリビルダー（q関数）またはシリアライズされたQueryState

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | Record<string, unknown>[] | クエリ結果の配列 |
| dependencies | string[] | 依存テーブル一覧（サブスクリプション用） |

### 出力先

API呼び出し元への戻り値

## 処理フロー

### 処理シーケンス

```
1. クエリ受け取り
   └─ Query.serialize()で状態を取得
2. スキーマ検証
   └─ テーブル・フィールドの存在確認
3. パス解決
   └─ JOINパスの解決とテーブル結合情報の構築
4. SELECT句コンパイル
   └─ フィールド参照と関数のSQL変換
5. WHERE句コンパイル
   └─ 条件式のSQL変換
6. GROUP BY/ORDER BY句コンパイル
7. SQL生成
   └─ 全パーツを結合してSQL文を生成
8. 実行
   └─ SQLiteで実行
9. 型変換
   └─ 結果を適切な型に変換
10. 返却
    └─ { data, dependencies }
```

### フローチャート

```mermaid
flowchart TD
    A[クエリ受け取り] --> B[スキーマ検証]
    B --> C{検証OK?}
    C -->|No| D[CompileError]
    C -->|Yes| E[パス解決]
    E --> F[SELECT句コンパイル]
    F --> G[WHERE句コンパイル]
    G --> H[GROUP BY/ORDER BYコンパイル]
    H --> I[SQL生成]
    I --> J[SQLite実行]
    J --> K[型変換]
    K --> L[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | tombstone除外 | tombstone=1のレコードはデフォルトで除外 | withDead=false時 |
| BR-02 | 自動ID追加 | 非集計クエリでは自動的にidフィールドを追加 | 集計でない場合 |
| BR-03 | 型推論 | パラメータの型は使用箇所から推論 | 名前付きパラメータ使用時 |
| BR-04 | 日付変換 | 日付は内部的に整数（YYYYMMDD）に変換 | 日付フィールド操作時 |

### 計算ロジック

**金額変換**: Actualは金額を100倍（センチユニット）で保持

**日付変換**: `dateToInt(date) = parseInt(date.replace(/-/g, ''))`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| クエリ実行 | 指定テーブル | SELECT | データ取得 |

### スキーマ定義

#### 主要テーブル

| テーブル名 | 説明 |
|-----------|------|
| transactions | 取引データ |
| accounts | 口座データ |
| categories | カテゴリデータ |
| category_groups | カテゴリグループ |
| payees | 支払先データ |
| schedules | スケジュールデータ |
| rules | ルールデータ |
| custom_reports | カスタムレポート |
| dashboard | ダッシュボード |
| zero_budgets / reflect_budgets | 予算データ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CompileError | 存在しないテーブル/フィールド | 正しいスキーマを参照 |
| - | CompileError | 不正な関数名 | サポートされる関数を使用 |
| - | CompileError | 型の不一致 | 適切な型に変換 |
| - | ParameterError | 必要なパラメータが未提供 | 全パラメータを提供 |

### リトライ仕様

特になし。

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

読み取り専用。SELECT文のみを生成・実行。

## パフォーマンス要件

- JOINは必要な場合のみ生成
- インデックスを活用できるクエリ構造を推奨

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

- SQLインジェクション防止のため、値は適切にエスケープ
- パラメータはプレースホルダー（?）で渡される

## 備考

- AQLはActual独自のクエリ言語であり、標準SQLとは構文が異なる
- スキーマ定義（schema/index.ts）で利用可能なフィールドが決まる

---

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

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

### 推奨読解順序

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

AQLで使用されるスキーマとクエリ状態を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema/index.ts | `packages/loot-core/src/server/aql/schema/index.ts` | スキーマ定義とテーブル構造 |
| 1-2 | query.ts | `packages/loot-core/src/shared/query.ts` | Query/QueryStateクラス |

**スキーマ定義のポイント**:
- **34-59行目**: transactions テーブル定義
- **60-67行目**: payees テーブル定義
- **68-80行目**: accounts テーブル定義
- **81-91行目**: categories テーブル定義
- **100-118行目**: schedules テーブル定義
- **142-166行目**: custom_reports テーブル定義
- **203-284行目**: schemaConfig（ビュー定義、カスタマイズ）

#### Step 2: コンパイラを理解する

クエリのSQL変換ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | compiler.ts | `packages/loot-core/src/server/aql/compiler.ts` | メインコンパイラ実装 |

**主要処理フロー**:
- **479-513行目**: `compileExpr` - 式のコンパイル（フィールド参照、パラメータ、リテラル）
- **515-637行目**: `compileFunction` - 関数のコンパイル（$sum, $count, $month等）
- **639-736行目**: `compileOp` - 演算子のコンパイル（$eq, $gte, $like等）
- **738-784行目**: `compileConditions` - 条件式のコンパイル
- **810-812行目**: `compileWhere` - WHERE句のコンパイル
- **867-915行目**: `compileSelect` - SELECT句のコンパイル
- **1055-1183行目**: `compileQuery` - メインコンパイル関数

#### Step 3: 実行エンジンを理解する

コンパイル済みクエリの実行ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | exec.ts | `packages/loot-core/src/server/aql/exec.ts` | クエリ実行 |

**主要処理フロー**:
- **27-34行目**: `applyTypes` - 結果の型変換
- **36-47行目**: `execQuery` - SQL実行
- **66-112行目**: `runCompiledAqlQuery` - コンパイル済みクエリの実行
- **114-129行目**: `compileAndRunAqlQuery` - コンパイル＆実行

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

AQL APIの公開インターフェースを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/loot-core/src/server/aql/index.ts` | AQLのエクスポート |

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

```
aqlQuery (index.ts:34)
    │
    └─ compileAndRunAqlQuery (exec.ts:114)
           │
           ├─ compileQuery (compiler.ts:1055)
           │      │
           │      ├─ compileSelect (compiler.ts:867)
           │      │      ├─ compileExpr (compiler.ts:479)
           │      │      └─ compileFunction (compiler.ts:515)
           │      │
           │      ├─ compileWhere (compiler.ts:810)
           │      │      ├─ compileConditions (compiler.ts:738)
           │      │      └─ compileOp (compiler.ts:639)
           │      │
           │      ├─ compileGroupBy (compiler.ts:917)
           │      │
           │      └─ compileOrderBy (compiler.ts:929)
           │
           └─ runCompiledAqlQuery (exec.ts:66)
                  │
                  └─ execQuery (exec.ts:36)
                         │
                         └─ db.all() (SQLite実行)
```

### データフロー図

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

Query/QueryState ───▶ compileQuery() ───▶ SQLPieces
                            │
     params ────────────────┤
                            ▼
                    defaultConstructQuery() ───▶ SQL文字列
                            │
                            ▼
                    db.all() ───▶ 生データ
                            │
                            ▼
                    applyTypes() ───▶ 型変換済みデータ
                            │
                            ▼
                    { data, dependencies }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/loot-core/src/server/aql/index.ts` | ソース | AQLエクスポート |
| compiler.ts | `packages/loot-core/src/server/aql/compiler.ts` | ソース | クエリコンパイラ |
| exec.ts | `packages/loot-core/src/server/aql/exec.ts` | ソース | クエリ実行 |
| schema/index.ts | `packages/loot-core/src/server/aql/schema/index.ts` | ソース | スキーマ定義 |
| schema/executors.ts | `packages/loot-core/src/server/aql/schema/executors.ts` | ソース | カスタム実行器 |
| schema-helpers.ts | `packages/loot-core/src/server/aql/schema-helpers.ts` | ソース | 型変換ヘルパー |
| views.ts | `packages/loot-core/src/server/aql/views.ts` | ソース | ビュー生成 |
| query.ts | `packages/loot-core/src/shared/query.ts` | ソース | Queryクラス |
