@aiquants/duckdb-helper 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/docs/guides/2026.04.11 [AI] development.md +291 -0
- package/docs/guides/2026.04.11 [AI] getting-started.md +278 -0
- package/docs/specs/2026.04.11 [AI] 00-overview.md +177 -0
- package/docs/specs/2026.04.11 [AI] 01-types.md +101 -0
- package/docs/specs/2026.04.11 [AI] 02-service.md +387 -0
- package/docs/specs/2026.04.11 [AI] 03-hooks.md +298 -0
- package/docs/specs/2026.04.11 [AI] 04-utils.md +220 -0
- package/docs/specs/2026.04.11 [AI] 05-logger.md +188 -0
- package/docs/specs/2026.04.11 [AI] 06-build-config.md +170 -0
- package/package.json +29 -32
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# @aiquants/duckdb-helper 開発ガイド
|
|
2
|
+
|
|
3
|
+
このガイドは、`@aiquants/duckdb-helper` パッケージの開発・保守・改修を行う開発者向けです。
|
|
4
|
+
|
|
5
|
+
## 1. 開発環境のセットアップ
|
|
6
|
+
|
|
7
|
+
### 前提条件
|
|
8
|
+
|
|
9
|
+
| ツール | バージョン |
|
|
10
|
+
|--------|-----------|
|
|
11
|
+
| Node.js | >= 16.0.0 |
|
|
12
|
+
| pnpm | >= 8.0.0 |
|
|
13
|
+
|
|
14
|
+
### 初回セットアップ
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# リポジトリルートで依存をインストール
|
|
18
|
+
pnpm install
|
|
19
|
+
|
|
20
|
+
# パッケージディレクトリに移動
|
|
21
|
+
cd packages/duckdb-helper
|
|
22
|
+
|
|
23
|
+
# ビルド確認
|
|
24
|
+
pnpm run build
|
|
25
|
+
|
|
26
|
+
# 型チェック
|
|
27
|
+
pnpm run type-check
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. 開発ワークフロー
|
|
31
|
+
|
|
32
|
+
### 日常的なコマンド
|
|
33
|
+
|
|
34
|
+
| やりたいこと | コマンド |
|
|
35
|
+
|-------------|---------|
|
|
36
|
+
| ビルド | `pnpm run build` |
|
|
37
|
+
| ウォッチモードで開発 | `pnpm run dev` |
|
|
38
|
+
| 型チェック | `pnpm run type-check` |
|
|
39
|
+
| Lint | `pnpm run lint` |
|
|
40
|
+
| Lint の自動修正 | `pnpm run lint:fix` |
|
|
41
|
+
| フォーマット | `pnpm run format` |
|
|
42
|
+
| フォーマットの自動修正 | `pnpm run format:fix` |
|
|
43
|
+
| Lint + フォーマット | `pnpm run check` |
|
|
44
|
+
| Lint + フォーマットの自動修正 | `pnpm run check:fix` |
|
|
45
|
+
| テスト | `pnpm run test` |
|
|
46
|
+
| テスト(カバレッジ付き) | `pnpm run test:coverage` |
|
|
47
|
+
| ライセンスチェック | `pnpm run license-check` |
|
|
48
|
+
|
|
49
|
+
### 開発中の推奨フロー
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# ターミナル1: ウォッチモードでビルド
|
|
53
|
+
pnpm run dev
|
|
54
|
+
|
|
55
|
+
# ターミナル2: 必要に応じてテスト
|
|
56
|
+
pnpm run test
|
|
57
|
+
|
|
58
|
+
# コミット前に
|
|
59
|
+
pnpm run check:fix && pnpm run type-check
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 3. ディレクトリ構成と役割
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
src/
|
|
66
|
+
├── index.ts # エントリーポイント
|
|
67
|
+
│ # → 公開 API をここで管理する
|
|
68
|
+
│ # → 新しいエクスポートを追加する際はここに追記
|
|
69
|
+
│
|
|
70
|
+
├── hooks/
|
|
71
|
+
│ └── useDuckDB.ts # React Hooks
|
|
72
|
+
│ # → React 固有のロジックはここに書く
|
|
73
|
+
│ # → サービス層の薄いラッパー
|
|
74
|
+
│
|
|
75
|
+
├── services/
|
|
76
|
+
│ └── duckdb.ts # DuckDBService(コアロジック)
|
|
77
|
+
│ # → DuckDB の初期化・クエリ実行など
|
|
78
|
+
│ # → React に依存しない純粋なサービス
|
|
79
|
+
│
|
|
80
|
+
├── types/
|
|
81
|
+
│ └── duckdb.ts # 型定義
|
|
82
|
+
│ # → インターフェースと型エイリアス
|
|
83
|
+
│
|
|
84
|
+
└── utils/
|
|
85
|
+
├── duckdb-helpers.ts # ヘルパー関数
|
|
86
|
+
│ # → クエリ結果の変換・検証ユーティリティ
|
|
87
|
+
│
|
|
88
|
+
└── simple-logger.ts # ロガー
|
|
89
|
+
# → ライブラリ内部のログ管理
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### ファイルの責務境界
|
|
93
|
+
|
|
94
|
+
| 変更したい内容 | 編集するファイル |
|
|
95
|
+
|---------------|----------------|
|
|
96
|
+
| DuckDB の初期化方法を変えたい | `services/duckdb.ts` の `internalInitializeWorker()` |
|
|
97
|
+
| 新しい DuckDB 拡張を追加したい | `services/duckdb.ts` の `internalInitializeWorker()` |
|
|
98
|
+
| クエリ実行のタイムアウトを変更したい | `services/duckdb.ts` の `executeQuery()` 内の `TIMEOUT_MS` |
|
|
99
|
+
| 型推定ロジックを改善したい | `services/duckdb.ts` の `inferColumnTypes()` |
|
|
100
|
+
| React Hook の API を変えたい | `hooks/useDuckDB.ts` |
|
|
101
|
+
| 新しいヘルパー関数を追加したい | `utils/duckdb-helpers.ts` + `index.ts` にエクスポート追加 |
|
|
102
|
+
| ログレベルのデフォルトを変えたい | `utils/simple-logger.ts` の static instance |
|
|
103
|
+
| 公開 API を追加したい | `index.ts` にエクスポートを追記 |
|
|
104
|
+
|
|
105
|
+
## 4. コードの読み方ガイド
|
|
106
|
+
|
|
107
|
+
### DuckDBService を理解する順序
|
|
108
|
+
|
|
109
|
+
1. **`getStatus()`** を読む → ステータスの4状態を理解する
|
|
110
|
+
2. **`initialize()`** を読む → 重複防止とタイムアウトの仕組みを理解する
|
|
111
|
+
3. **`internalInitializeWorker()`** を読む → 実際の WASM 初期化の流れを理解する
|
|
112
|
+
4. **`getConnection()`** を読む → コネクションの遅延初期化を理解する
|
|
113
|
+
5. **`executeQuery()`** を読む → クエリ実行とタイムアウトを理解する
|
|
114
|
+
6. **`createTableFromData()`** を読む → 型推定とバッチ INSERT を理解する
|
|
115
|
+
|
|
116
|
+
### useDuckDB を理解する順序
|
|
117
|
+
|
|
118
|
+
1. **`UseDuckDBResult` インターフェース** を読む → 戻り値の全体像を把握する
|
|
119
|
+
2. **`useDuckDB` 関数** の useState/useEffect を読む → ステータス同期の仕組みを理解する
|
|
120
|
+
3. **各操作関数(executeQuery 等)** を読む → すべて `useCallback` でメモ化されていることを確認する
|
|
121
|
+
4. **`useDuckDBQuery`** を読む → `useDuckDB` の上に構築されたシンプルなフック
|
|
122
|
+
|
|
123
|
+
## 5. 新機能の追加手順
|
|
124
|
+
|
|
125
|
+
### 例: 新しいヘルパー関数を追加する
|
|
126
|
+
|
|
127
|
+
**ステップ1**: `src/utils/duckdb-helpers.ts` に関数を追加
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
/**
|
|
131
|
+
* Get first N rows from DuckDB result
|
|
132
|
+
* DuckDB 結果から最初の N 行を取得します
|
|
133
|
+
*/
|
|
134
|
+
export const getFirstRows = <T>(result: unknown, count: number): T[] => {
|
|
135
|
+
const rows = duckdbTableToArray<T>(result)
|
|
136
|
+
return rows.slice(0, count)
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**ステップ2**: `src/index.ts` にエクスポートを追加
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
export {
|
|
144
|
+
duckdbTableToArray,
|
|
145
|
+
getDuckDBColumnCount,
|
|
146
|
+
getDuckDBRowCount,
|
|
147
|
+
isDuckDBTable,
|
|
148
|
+
getFirstRows, // ← 追加
|
|
149
|
+
} from "./utils/duckdb-helpers"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**ステップ3**: テストを追加
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// tests/unit/duckdb-helpers.test.ts
|
|
156
|
+
import { describe, expect, it } from "vitest"
|
|
157
|
+
import { getFirstRows } from "../../src/utils/duckdb-helpers"
|
|
158
|
+
|
|
159
|
+
describe("getFirstRows", () => {
|
|
160
|
+
it("should return first N rows", () => {
|
|
161
|
+
const mockResult = {
|
|
162
|
+
toArray: () => [{ id: 1 }, { id: 2 }, { id: 3 }],
|
|
163
|
+
numRows: 3,
|
|
164
|
+
numCols: 1,
|
|
165
|
+
}
|
|
166
|
+
expect(getFirstRows(mockResult, 2)).toEqual([{ id: 1 }, { id: 2 }])
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**ステップ4**: ビルド・テスト・型チェック
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pnpm run type-check && pnpm run test && pnpm run build
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 6. テストのガイドライン
|
|
178
|
+
|
|
179
|
+
### テストフレームワーク
|
|
180
|
+
|
|
181
|
+
- **Vitest** を使用
|
|
182
|
+
- 設定は `vitest.config.ts`(またはデフォルト設定)
|
|
183
|
+
|
|
184
|
+
### テストの分類
|
|
185
|
+
|
|
186
|
+
| ディレクトリ | 種類 | 説明 |
|
|
187
|
+
|-------------|------|------|
|
|
188
|
+
| `tests/unit/` | 単体テスト | 個々の関数・クラスのテスト |
|
|
189
|
+
| `tests/integration/` | 統合テスト | 複数モジュールの連携テスト |
|
|
190
|
+
|
|
191
|
+
### テスト時の注意
|
|
192
|
+
|
|
193
|
+
- DuckDB-WASM はブラウザ環境が必要なため、Service のフルテストはブラウザテストランナーが必要
|
|
194
|
+
- ユーティリティ関数(`duckdb-helpers.ts`)は Node.js 環境でテスト可能
|
|
195
|
+
- React Hooks のテストには `@testing-library/react` の使用を推奨
|
|
196
|
+
|
|
197
|
+
## 7. リリース手順
|
|
198
|
+
|
|
199
|
+
### バージョンアップ
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# パッチ版 (1.4.1 → 1.4.2): バグ修正
|
|
203
|
+
pnpm run publish:patch
|
|
204
|
+
|
|
205
|
+
# マイナー版 (1.4.1 → 1.5.0): 機能追加(後方互換あり)
|
|
206
|
+
pnpm run publish:minor
|
|
207
|
+
|
|
208
|
+
# メジャー版 (1.4.1 → 2.0.0): 破壊的変更
|
|
209
|
+
pnpm run publish:major
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### リリース前チェックリスト
|
|
213
|
+
|
|
214
|
+
- [ ] 型チェックが通ること(`pnpm run type-check`)
|
|
215
|
+
- [ ] テストが通ること(`pnpm run test`)
|
|
216
|
+
- [ ] Lint エラーがないこと(`pnpm run check`)
|
|
217
|
+
- [ ] ビルドが成功すること(`pnpm run build`)
|
|
218
|
+
- [ ] ライセンスチェックが通ること(`pnpm run license-check`)
|
|
219
|
+
- [ ] `index.ts` のエクスポートが正しいこと
|
|
220
|
+
- [ ] 破壊的変更がある場合はメジャーバージョンアップ
|
|
221
|
+
|
|
222
|
+
### `prepublishOnly` スクリプト
|
|
223
|
+
|
|
224
|
+
`pnpm publish` 実行時に自動で以下が実行される:
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
clean → typecheck → test(--if-present) → build
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`test` は `--if-present` フラグ付きで実行されるため、テストスクリプトが定義されていない場合はスキップされる。それ以外のステップが失敗すると公開は中断される。
|
|
231
|
+
|
|
232
|
+
## 8. アーキテクチャ上の注意点
|
|
233
|
+
|
|
234
|
+
### シングルトンの影響
|
|
235
|
+
|
|
236
|
+
`DuckDBService` はシングルトンなので:
|
|
237
|
+
|
|
238
|
+
- アプリ全体で1つの DuckDB インスタンスを共有する
|
|
239
|
+
- テスト間で状態が残る可能性がある → `DuckDBService.resetInstance()` でリセット
|
|
240
|
+
- SSR 環境では Web Worker が使えないため初期化に失敗する
|
|
241
|
+
|
|
242
|
+
### コネクションの共有
|
|
243
|
+
|
|
244
|
+
- `getConnection()` は1つのコネクションを返す
|
|
245
|
+
- 複数のコンポーネントが同時にクエリを実行しても、内部では同一コネクションが使われる
|
|
246
|
+
- DuckDB-WASM は単一コネクションでの逐次実行を前提としている
|
|
247
|
+
|
|
248
|
+
### メモリ管理
|
|
249
|
+
|
|
250
|
+
- WASM インスタンスはメモリを消費する
|
|
251
|
+
- 不要になったら `cleanup()` でリソースを解放する
|
|
252
|
+
- テーブルデータは DuckDB の内部メモリに保持される
|
|
253
|
+
|
|
254
|
+
## 9. よくある質問
|
|
255
|
+
|
|
256
|
+
### Q: 新しい DuckDB 拡張を追加するには?
|
|
257
|
+
|
|
258
|
+
`services/duckdb.ts` の `internalInitializeWorker()` 内、splink_udfs のインストール箇所の後に追加する:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
try {
|
|
262
|
+
await connection.query("INSTALL 拡張名 FROM community;")
|
|
263
|
+
await connection.query("LOAD 拡張名;")
|
|
264
|
+
Logger.info("✅ 拡張名 loaded")
|
|
265
|
+
} catch (error) {
|
|
266
|
+
Logger.warn("⚠️ Failed to load 拡張名:", error)
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Q: Logger を公開 API にしたい
|
|
271
|
+
|
|
272
|
+
`src/index.ts` にエクスポートを追加する:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
export { Logger, LogLevel } from "./utils/simple-logger"
|
|
276
|
+
export type { ILogger } from "./utils/simple-logger"
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Q: React なしで使いたい
|
|
280
|
+
|
|
281
|
+
`DuckDBService` は React に依存していないので、そのまま使える:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { DuckDBService } from '@aiquants/duckdb-helper'
|
|
285
|
+
|
|
286
|
+
const service = DuckDBService.getInstance()
|
|
287
|
+
await service.initialize()
|
|
288
|
+
const result = await service.executeQuery('SELECT 1')
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
React はオプショナルなピア依存なので、インストールしなくてもパッケージの利用は可能。
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# @aiquants/duckdb-helper 入門ガイド
|
|
2
|
+
|
|
3
|
+
このガイドは、`@aiquants/duckdb-helper` を初めて使う開発者向けのステップバイステップの導入手順書です。
|
|
4
|
+
|
|
5
|
+
## 前提知識
|
|
6
|
+
|
|
7
|
+
- TypeScript / JavaScript の基本
|
|
8
|
+
- React の基本(Hooks の概念)
|
|
9
|
+
- SQL の基本構文
|
|
10
|
+
|
|
11
|
+
## 1. DuckDB とは
|
|
12
|
+
|
|
13
|
+
DuckDB は高速な列指向の分析用データベースエンジンです。通常はサーバーで動作しますが、**DuckDB-WASM** を使うとブラウザ上で直接 SQL を実行できます。
|
|
14
|
+
|
|
15
|
+
このライブラリは、DuckDB-WASM のセットアップと利用を簡単にするためのヘルパーです。
|
|
16
|
+
|
|
17
|
+
## 2. インストール
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# pnpm(推奨)
|
|
21
|
+
pnpm add @aiquants/duckdb-helper
|
|
22
|
+
|
|
23
|
+
# npm
|
|
24
|
+
npm install @aiquants/duckdb-helper
|
|
25
|
+
|
|
26
|
+
# yarn
|
|
27
|
+
yarn add @aiquants/duckdb-helper
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
React Hooks を使う場合は、React 18 以上が必要です。
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add react react-dom
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 3. 最初のクエリを実行する
|
|
37
|
+
|
|
38
|
+
### 3.1 サービスを直接使う方法(React なし)
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { duckDBService, duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
42
|
+
|
|
43
|
+
async function main() {
|
|
44
|
+
// ステップ1: DuckDB を初期化する
|
|
45
|
+
// (WASM のダウンロードがあるため、初回は数秒かかる)
|
|
46
|
+
await duckDBService.initialize()
|
|
47
|
+
|
|
48
|
+
// ステップ2: SQL を実行する
|
|
49
|
+
const result = await duckDBService.executeQuery('SELECT 42 AS answer')
|
|
50
|
+
|
|
51
|
+
// ステップ3: 結果を JavaScript 配列に変換する
|
|
52
|
+
const rows = duckdbTableToArray<{ answer: number }>(result)
|
|
53
|
+
|
|
54
|
+
console.log(rows)
|
|
55
|
+
// → [{ answer: 42 }]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3.2 React Hook を使う方法(推奨)
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useDuckDBQuery, duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
65
|
+
|
|
66
|
+
interface QueryResult {
|
|
67
|
+
answer: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const MyFirstComponent = () => {
|
|
71
|
+
// SQL を渡すだけで自動的に初期化 → 実行 → 結果取得
|
|
72
|
+
const { data, loading, error } = useDuckDBQuery<unknown>(
|
|
73
|
+
'SELECT 42 AS answer'
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if (loading) return <p>読み込み中...</p>
|
|
77
|
+
if (error) return <p>エラー: {error}</p>
|
|
78
|
+
if (!data) return <p>データなし</p>
|
|
79
|
+
|
|
80
|
+
const rows = duckdbTableToArray<QueryResult>(data)
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div>
|
|
84
|
+
<h1>初めてのクエリ結果</h1>
|
|
85
|
+
<p>答え: {rows[0]?.answer}</p>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 4. データをテーブルに投入する
|
|
92
|
+
|
|
93
|
+
JavaScript の配列データを DuckDB テーブルとして登録し、SQL で分析できます。
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { useDuckDB, duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
97
|
+
import { useEffect, useState } from 'react'
|
|
98
|
+
|
|
99
|
+
// サンプルデータ
|
|
100
|
+
const sampleData = [
|
|
101
|
+
{ id: 1, name: '田中太郎', age: 30, department: '開発' },
|
|
102
|
+
{ id: 2, name: '鈴木花子', age: 25, department: '営業' },
|
|
103
|
+
{ id: 3, name: '佐藤次郎', age: 35, department: '開発' },
|
|
104
|
+
{ id: 4, name: '高橋美咲', age: 28, department: '人事' },
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
const DataAnalysis = () => {
|
|
108
|
+
const { isReady, createTableFromData, executeQuery } = useDuckDB()
|
|
109
|
+
const [result, setResult] = useState<string>('')
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const analyze = async () => {
|
|
113
|
+
if (!isReady) return
|
|
114
|
+
|
|
115
|
+
// ステップ1: データをテーブルとして作成
|
|
116
|
+
await createTableFromData('employees', sampleData, {
|
|
117
|
+
dropIfExists: true, // 既存テーブルがあれば削除
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// ステップ2: SQL で分析
|
|
121
|
+
const queryResult = await executeQuery(
|
|
122
|
+
'SELECT department, COUNT(*) as count, AVG(age) as avg_age FROM employees GROUP BY department'
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const rows = duckdbTableToArray<{
|
|
126
|
+
department: string
|
|
127
|
+
count: number
|
|
128
|
+
avg_age: number
|
|
129
|
+
}>(queryResult)
|
|
130
|
+
|
|
131
|
+
setResult(JSON.stringify(rows, null, 2))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
analyze()
|
|
135
|
+
}, [isReady, createTableFromData, executeQuery])
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div>
|
|
139
|
+
<h1>部署別分析</h1>
|
|
140
|
+
<pre>{result || '分析中...'}</pre>
|
|
141
|
+
</div>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 5. 初期化の仕組みを理解する
|
|
147
|
+
|
|
148
|
+
DuckDB の初期化には以下のステップがあり、**初回は数秒かかります**。
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
1. CDN から WASM バンドルをダウンロード
|
|
152
|
+
2. ブラウザに最適なバンドルを自動選択
|
|
153
|
+
3. Web Worker を作成
|
|
154
|
+
4. WASM モジュールを読み込み
|
|
155
|
+
5. splink_udfs 拡張をインストール(オプション)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### ステータスの遷移
|
|
159
|
+
|
|
160
|
+
```mermaid
|
|
161
|
+
stateDiagram-v2
|
|
162
|
+
state "not-initialized" as not_init
|
|
163
|
+
[*] --> not_init
|
|
164
|
+
not_init --> initializing : initialize()
|
|
165
|
+
initializing --> ready : 成功
|
|
166
|
+
initializing --> error : 失敗
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 初期化を待つパターン
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
const MyComponent = () => {
|
|
173
|
+
const { status, isReady, error } = useDuckDB()
|
|
174
|
+
|
|
175
|
+
// ステータスに応じた UI の出し分け
|
|
176
|
+
switch (status) {
|
|
177
|
+
case 'not-initialized':
|
|
178
|
+
return <p>待機中...</p>
|
|
179
|
+
case 'initializing':
|
|
180
|
+
return <p>DuckDB を準備しています...</p>
|
|
181
|
+
case 'error':
|
|
182
|
+
return <p>エラーが発生しました: {error}</p>
|
|
183
|
+
case 'ready':
|
|
184
|
+
return <p>準備完了!</p>
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 6. よくあるパターン
|
|
190
|
+
|
|
191
|
+
### パターン A: パラメータでクエリを変える
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
const UserDetail = ({ userId }: { userId: number }) => {
|
|
195
|
+
const { data, loading } = useDuckDBQuery(
|
|
196
|
+
`SELECT * FROM users WHERE id = ${userId}`,
|
|
197
|
+
[userId] // ← userId が変わるとクエリが再実行される
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if (loading) return <p>読み込み中...</p>
|
|
201
|
+
// ...
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### パターン B: 複数クエリを順番に実行
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const { executeQueries } = useDuckDB()
|
|
209
|
+
|
|
210
|
+
// CREATE → INSERT → SELECT を順番に実行
|
|
211
|
+
const results = await executeQueries([
|
|
212
|
+
"CREATE TABLE temp (id INTEGER, value VARCHAR)",
|
|
213
|
+
"INSERT INTO temp VALUES (1, 'hello'), (2, 'world')",
|
|
214
|
+
"SELECT * FROM temp"
|
|
215
|
+
])
|
|
216
|
+
|
|
217
|
+
// results[2] が SELECT の結果
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### パターン C: 手動で初期化タイミングを制御
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
const LazyInit = () => {
|
|
224
|
+
const { initialize, status, isReady } = useDuckDB(false) // false = 自動初期化しない
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div>
|
|
228
|
+
<p>Status: {status}</p>
|
|
229
|
+
{!isReady && (
|
|
230
|
+
<button onClick={() => initialize()}>
|
|
231
|
+
DuckDB を初期化する
|
|
232
|
+
</button>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 7. トラブルシューティング
|
|
240
|
+
|
|
241
|
+
### Q: 初期化が失敗する
|
|
242
|
+
|
|
243
|
+
**原因**: ブラウザが Web Worker または WASM をサポートしていない、もしくはネットワークが CDN にアクセスできない。
|
|
244
|
+
|
|
245
|
+
**対処**:
|
|
246
|
+
1. ブラウザの DevTools コンソールでエラーを確認
|
|
247
|
+
2. jsDelivr CDN(`cdn.jsdelivr.net`)へのアクセスが許可されているか確認
|
|
248
|
+
3. CORS やプロキシの設定を確認
|
|
249
|
+
|
|
250
|
+
### Q: クエリが "DuckDB is not ready" エラーになる
|
|
251
|
+
|
|
252
|
+
**原因**: 初期化が完了する前にクエリを実行しようとしている。
|
|
253
|
+
|
|
254
|
+
**対処**:
|
|
255
|
+
```tsx
|
|
256
|
+
// isReady をチェックしてから実行
|
|
257
|
+
if (isReady) {
|
|
258
|
+
const result = await executeQuery('SELECT 1')
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Q: 大量データの投入が遅い
|
|
263
|
+
|
|
264
|
+
**対処**:
|
|
265
|
+
- `createTableFromData` は内部で1000行ずつバッチ INSERT する
|
|
266
|
+
- データが非常に大きい場合は、CSV/Parquet ファイルを直接読み込む DuckDB のネイティブ機能の使用を検討
|
|
267
|
+
|
|
268
|
+
### Q: 初期化ログが表示されない
|
|
269
|
+
|
|
270
|
+
**原因**: デフォルトのログレベルが `WARN` のため、`info` レベルのログは非表示。
|
|
271
|
+
|
|
272
|
+
**対処**: `Logger` と `LogLevel` は現在公開 API としてエクスポートされておらず、`package.json` の `exports` にもサブパスが未定義のため、利用側から直接ログレベルを変更することはできない。ログレベル制御が必要な場合は、パッケージ側で `index.ts` にエクスポートを追加する対応が必要(詳細は [Logger 仕様書](../spec/2026.04.11%20%5BAI%5D%2005-logger.md) を参照)。
|
|
273
|
+
|
|
274
|
+
## 8. 次のステップ
|
|
275
|
+
|
|
276
|
+
- 各モジュールの詳細は [設計書](../spec/) を参照
|
|
277
|
+
- API リファレンスは [useDuckDB.md](../useDuckDB.md) を参照
|
|
278
|
+
- 開発・保守作業については [開発ガイド](./2026.04.11%20%5BAI%5D%20development.md) を参照
|