IndexedDB の主な特徴と基本用語
====================

この記事では IndexedDB の主な特徴を説明し、IndexedDB API の理解に関連する重要な用語を紹介しています。

また、以下の記事も参考になるでしょう。

* API の使用方法に関する詳細なチュートリアルについては、IndexedDB の使用を参照してください。
* IndexedDB API のリファレンス文書については、IndexedDB API のメイン記事と、IndexedDB で使用されるオブジェクトの型を記したそのサブページを参照してください。
* ブラウザーがバックグラウンドでデータを保存する方法の詳細については、ブラウザーのストレージ制限と削除基準を参照してください。
主な特徴
----

IndexedDB は、ユーザーのブラウザー内にデータを永続的に保存するための方法です。ネットワークの利用可否にかかわらず、豊富なクエリー機能を備えたウェブアプリケーションを作成できるため、オンラインでもオフラインでも動作します。IndexedDB は、大量のデータを保存するアプリケーション (貸し出し用図書館の DVD カタログなど) や、インターネットへの持続的な接続を必要としないアプリケーション (メールクライアント、ToDo リスト、メモ帳など) に有効です。

IndexedDB では、「キー」に基づいてインデックス化されたオブジェクトを保存・取得することができます。データベースへの変更は、すべてトランザクション内で行われます。多くのウェブストレージ技術と同様に、IndexedDB は同一オリジンポリシーに従っています。そのため、ドメイン内の保存データにはアクセスできますが、異なるドメイン間のデータにはアクセスできません。

他の種類のデータベースでの作業を前提としていると、IndexedDB での作業に戸惑うことがあるかもしれません。そのため、以下のような IndexedDB の主な特徴を覚えておくことが重要です。

* **IndexedDB データベースは、キーと値の組を格納します。** 値は複雑な構造のオブジェクトで、キーはそのオブジェクトのプロパティです。オブジェクトの任意のプロパティを使用するインデックスを作成して、迅速な検索や並べ替えされた列挙を行うことができます。キーにはバイナリーオブジェクトを使用することができます。
* **IndexedDB は、トランザクションデータベースモデルに基づいて構築されています。** IndexedDB で行うことは、常にトランザクションのコンテキストで行われます。IndexedDB API には、インデックス、テーブル、カーソルなどを表す多くのオブジェクトが用意されていますが、これらはそれぞれ特定のトランザクションに関連付けられています。そのため、トランザクションの外でコマンドを実行したり、カーソルを開いたりすることはできません。トランザクションには十分に定義された有効期間があるため、トランザクションが完了した後に使用しようとすると、例外が発生します。また、トランザクションは自動コミットされ、手動でコミットすることはできません。
 このトランザクションモデルは、ユーザーがウェブアプリケーションの 2 つのインスタンスを 2 つの異なるタブで同時に開いた場合のことを考えると、とても有用です。トランザクション操作がなければ、 2 つのインスタンスが互いの変更に干渉してしまう可能性があります。データベースのトランザクションに慣れていない方は、Wikipedia のトランザクションに関する記事をご覧ください。また、「定義」の章のトランザクションもご覧ください。
* **IndexedDB API は、ほとんどが非同期です。** API は値を返すことでデータを提供するわけではありません。コールバック関数を渡す必要があります。同期的な方法でデータベースに値を「格納」したり、データベースから値を「取り出す」ことはしません。代わりに、データベース操作を「リクエスト」します。操作が終了すると DOM イベントで通知され、そのイベントの種類によって操作が成功したか失敗したかが分かります。最初は少し複雑に聞こえるかもしれませんが、そこには健全性を保つための対策が組み込まれています。これは、XMLHttpRequest が動作する方法と大きな違いはありません。
* **IndexedDB は多くのリクエストを使用します。** リクエストは、前述の成功または失敗の DOM イベントを受け取るオブジェクトです。このオブジェクトには `onsuccess` と `onerror` のプロパティがあり、`addEventListener()` と `removeEventListener()` を呼び出すことができます。また、`readyState`、`result`、`errorCode` の各プロパティがあり、リクエストの状態を知ることができます。`result` プロパティは、リクエストの生成方法 (例えば `IDBCursor` インスタンスや、データベースに挿入したばかりの値のキー) に応じて、さまざまなものになるため、特に魔法のようなものです。
* **IndexedDB は DOM イベントを使って、結果が利用可能になったことを通知します。** DOM イベントには、必ず `type` プロパティがあります (IndexedDB では、最も一般的に `"success"` または `"error"` に設定されます)。また、DOM イベントには、イベントの目的地を示す `target` プロパティがあります。ほとんどの場合、イベントの `target` は、何らかのデータベース操作の結果として生成された `IDBRequest` オブジェクトです。成功イベントはバブルアップしませんし、キャンセルもできません。一方、エラーイベントはバブリングします、キャンセルも可能です。これは非常に重要なことで、エラーイベントはキャンセルされない限り、実行中のトランザクションを中断します。
* **IndexedDB はオブジェクト指向です。** IndexedDB は、行と列の集合体であるテーブルを持つリレーショナルデータベースではありません。この重要かつ根本的な違いは、アプリケーションの設計・構築方法に影響します。
 従来のリレーショナルデータストアでは、データの行と名前の付いたデータの列の集合体を格納するテーブルがあります。一方、IndexedDB では、データの種類に応じてオブジェクトストアを作成し、そのストアに JavaScript オブジェクトを永続化する必要があります。各オブジェクトストアには、クエリや反復処理を効率的に行うためのインデックスのコレクションを持つことができます。オブジェクト指向データベース管理システムに慣れていない方は、Wikipedia のオブジェクトデータベースの記事をお読みください。
* **IndexedDB は Structured Query Language (SQL) を使用しません。** インデックスに対するクエリーを使用してカーソルを生成し、そのカーソルを使用して結果セットを反復処理します。NoSQL システムについてよく知らない方は、Wikipedia の NoSQL に関する記事をご覧ください。
* **IndexedDB は、同一オリジンポリシーを採用しています**。オリジンとは、スクリプトを実行している文書の URL のドメイン、アプリケーション層のプロトコル、およびポートのことです。各オリジンには、それぞれ関連するデータベースのセットがあります。すべてのデータベースには、オリジン内で識別するための名前があります。
 IndexedDB にはセキュリティ境界が課せられており、アプリケーションが異なるオリジンのデータにアクセスすることを防ぎます。例えば、`http://www.example.com/app/` のアプリやページは、`http://www.example.com/dir/` からはオリジンが同じであるためデータを取得することができますが、`http://www.example.com:8080/dir/` (ポートが異なる) や `https://www.example.com/dir/` (プロトコルが異なる) からは、オリジンが異なるためデータを取得することはできません。

**メモ:** サードパーティのウィンドウコンテンツ (例: `<iframe>` コンテンツ) は、ブラウザーがサードパーティのクッキーを受け入れないように設定されていない限り、埋め込まれたオリジンの IndexedDB ストアにアクセスすることができます (Firefox バグ 1147821 を参照)。
### 制限事項

IndexedDB は、クライアントサイドのストレージを必要とするほとんどのケースをカバーするように設計されています。しかし、以下のようないくつかのケースには対応していません。

* 国際化に対応した並べ替え。すべての言語で文字列が同じように並べ替えされるわけではないので、国際化に対応した並べ替えには対応していません。データベースは特定の国際化に対応した順序でデータを保存することはできませんが、データベースから読み取ったデータを自分で並べ替えすることはできます。ただし、Firefox 43 以降、実験的なフラグを有効にすることで、ロケールを考慮した並べ替えが可能になっています (現在は Firefox のみ)。
* 同期。この API は、サーバー側のデータベースとの同期を行うようには設計されていません。クライアント側の indexedDB データベースとサーバー側のデータベースを同期させるコードを書く必要があります。
* 全文検索。この API には、 SQL の `LIKE` 演算子に相当するものがありません。

また、以下のような条件でブラウザーがデータベースを消去することがあるので注意が必要です。

* ユーザーが消去を要求した場合。多くのブラウザーには、Cookie、ブックマーク、保存されたパスワード、IndexedDB データなど、特定のウェブサイトに保存されたすべてのデータを消去できる設定があります。
* ブラウザーがプライベートブラウジングモードになっている場合。一部のブラウザーには、「プライベートブラウジング」 (Firefox) または「シークレット」 (Chrome) モードがあります。セッションの終了時に、ブラウザーはデータベースを消去します。
* ディスクまたはクォータの容量の上限に達した場合。
* データが破損した場合。
* この機能に対して互換性のない変更が行われた場合。

正確な状況やブラウザーの機能は時間とともに変化しますが、ブラウザーベンダーの一般的な考え方は、可能な限りデータを残すために最善の努力をするというものです。

主な用語
----

この節では、IndexedDB API の理解に関連する主な用語を定義および説明します。

### データベース

#### データベース

普通 1 つ以上の*オブジェクトストア*で構成される、情報の格納庫です。それぞれのデータベースには以下のものがあります。

* 名前。これは、特定のオリジン内でデータベースを識別し、その生涯を通じて一定です。名前は任意の文字列値 (空文字列を含む) とすることができます。
* 現在の*バージョン*。データベースが最初に作成されたとき、特に指定がなければそのバージョンは整数の 1 です。各データベースは、常に 1 つのバージョンしか持つことができません。

#### データベースコネクション

データベースを開くために作成される操作。指定されたデータベースは、同時に複数の接続を保有することができます。

#### 永続性

Firefox では、IndexedDB は以前は**永続性**がありました。つまり、読み書きトランザクションにおいて、すべてのデータがディスクにフラッシュされたことが保証されたときにのみ、`complete` が発行されていました。

Firefox 40 では、IndexedDB トランザクションは、パフォーマンスを向上させるために、他の IndexedDB に対応するブラウザーと同様に永続性の保証を緩和しています (Firefox バグ 1112702 を参照)。この場合、 `complete` イベントは、OS がデータの書き込みを指示した後に発生しますが、そのデータが実際にディスクにフラッシュされる前に発生する可能性もあります。そのため、イベントは以前よりも早く配信されるかもしれませんが、データがディスクにフラッシュされる前に OS がクラッシュしたり、システムの電源が切れたりすると、トランザクション全体が失われてしまう可能性がわずかながらあります。このような致命的な事象は稀であるため、ほとんどの人はこれ以上気にする必要はありません。

**メモ:** Firefox では、何らかの理由で永続性を確保したい場合 (たとえば、後で再計算できない重要なデータを保存している場合)、実験的な (標準外の) `readwriteflush` モードを使用してトランザクションを作成することで、`complete` イベントを配信する前にトランザクションをディスクに強制的にフラッシュさせることができます(`IDBDatabase.transaction` を参照)。これは現在実験的なものであり、`about:config` で `dom.indexedDB.experimental` 設定項目が `true` に設定されている場合にのみ使用できます。

#### インデックス

インデックスは、参照オブジェクトストアと呼ばれる別のオブジェクトストアにあるレコードを探すために特化したオブジェクトストアです。インデックスは永続的なキー-値ストレージで、そのレコードの値の部分が参照するオブジェクトストアのレコードのキー部分となります。インデックス内のレコードは、参照されるオブジェクトストア内のレコードが挿入、更新、または削除されるたびに、自動的に入力されます。インデックス内の各レコードは、参照するオブジェクトストア内の1つのレコードのみを参照することができますが、複数のインデックスが同じオブジェクトストアを参照することは可能です。オブジェクトストアが変更されると、そのオブジェクトストアを参照しているすべてのインデックスが自動的に更新されます。

また、キーを使用してオブジェクトストア内のレコードを見いだすこともできます。

インデックスの使用について詳しくは、 IndexedDB の使用を参照してください。インデックスについてのリファレンス記事は、 IDBKeyRange を参照してください。

#### オブジェクトストア

データベースにデータを保存する仕組みです。オブジェクトストアはレコードを持続的に保持しており、これはキーと値の組です。オブジェクトストア内のレコードは、*キー* によって昇順に整列して保存されています。

すべてのオブジェクトストアには、そのデータベース内で一意となる名前が必要です。オブジェクトストアは、オプションで *キージェネレーター* と *キーパス* を持つことができます。オブジェクトストアにキーパスがある場合は *インラインキー*、ない場合は *アウトオブラインキー* が使用されます。

オブジェクトストアのリファレンス文書は、 `IDBObjectStore` を参照してください。

#### リクエスト

データベースへの読み書きが行われる処理。すべてのリクエストは、 1 つの読み取りまたは書き込み処理を表します。

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

特定のデータベースで行う、原子性を持つデータアクセスやデータ変更の操作のセットです。これは、データベース内のデータと対話する手段です。実際は、データベース内のデータの読み取りや変更はトランザクション内で実施しなければなりません。

書き込みトランザクションの*スコープ*が重ならない限り、ひとつのデータベース接続で同時に複数のアクティブなトランザクションが存在できます。トランザクションのスコープは生成時に定義され、トランザクションがどのオブジェクトストアと対話できるかや、トランザクションの持続期間にわたって保持し続けるかを示します。よって例えば、データベース接続で `flyingMonkey` オブジェクトストアのみ対象とするスコープを持つ書き込みトランザクションがすでに存在するとき、`unicornCentaur` オブジェクトストアや `unicornPegasus` オブジェクトストアをスコープで持つ別のトランザクションを開始できます。読み取りトランザクションは、スコープが重なっていても複数実行できます。

トランザクションは持続期間が短いものを除き、長時間のトランザクションがストレージ資源をロックする状況から解放するために、ブラウザーが終了させることができます。トランザクションは中止させることができ、トランザクションによるデータベースの変更箇所はロールバックされます。また、開始するトランザクションや中止するトランザクションを待つ必要はありません。

トランザクションには `readwrite`、`readonly`、`versionchange` の 3 つのモードがあります。オブジェクトストアやインデックスの生成および削除は、`versionchange` トランザクションを使用する場合に限り実行できます。トランザクションのタイプについて詳しくは、IndexedDB のリファレンスをご覧ください。

すべての操作はトランザクション内で発生しますので、トランザクションは IndexedDB の重要な概念です。トランザクションについて、特にバージョン付けとの関係については、`IDBTransaction` および関連文書をご覧ください。ここにリファレンス文書もあります。

#### バージョン

始めにデータベースを生成したとき、バージョンは整数の 1 になります。それぞれのデータベースは、一度に 1 つのバージョンを持ちます。一度に複数のバージョンを持つことはできません。バージョンを変更する唯一の方法は、現在のバージョンより大きなバージョンでデータベースを開くことです。

### キーと値

#### インラインキー

保存される値の一部として保存されるキーです。これは **キーパス** を使用して見つけます。インラインキーは、ジェネレーターを使用して生成できます。キーが生成されると、キーパスを使用してキーを値の中に保存したり、キーとして使用したりすることができます。

#### キー

オブジェクトストアに保存された値は、このデータ値によって編成および取り出しされます。オブジェクトストアは **キージェネレーター**、*キーパス*、明示的に指定した値の、3 種類の生成源のいずれかからキーを得られます。キーは、自身の前にあるものより大きな数値を持つデータ型であることが必要です。オブジェクトストア内の各レコードはオブジェクトストア内で一意のキーを持たなければならず、オブジェクトストア内で複数のレコードが同じキーを持つことはできません。

キーは 文字列、date、浮動小数点数値、配列のいずれかの型を使用できます。配列では、キーは空の値から無限大までの範囲を使用できます。また、配列の中に配列を含めることができます。文字列または整数値のキーしか使用できないという条件はありません。

代わりに、インデックスを使用してオブジェクトストア内のレコードを検索することもできます。

#### キージェネレーター

指定した順序で新たなキーを生成する仕組みです。オブジェクトストアがキージェネレーターを持たない場合は、保存するレコードのキーをアプリケーションが提供しなければなりません。ジェネレーターはストア間で共有しません。これはむしろブラウザーの実装の細部であり、Web 開発において実際にはキージェネレーターの生成やアクセスは行いません。

#### キーパス

オブジェクトストアやインデックスのどこからブラウザーがキーを取り出すべきかを定義します。有効なキーパスは空文字列、JavaScript の識別子、ピリオドで区切られた複数の JavaScript の識別子、あるいはそれらを収めた配列のいずれかを含むことができます。空白を含むことはできません。

#### アウトオブラインキー

保存する値とは別に保存されるキーです。

#### 値

それぞれのレコードは値を持っており、論理値、数値、文字列、date、オブジェクト、配列、正規表現、undefined、null を含む、JavaScript で表現可能なものをどれでも含むことができます。

オブジェクトまたは配列を保存する場合は、それらのプロパティや値もまた、有効な値をどれでも持つことができます。

また、Blob やファイルも保存可能です。仕様書 をご覧ください。

### レンジとスコープ

#### カーソル

*キーレンジ*に属する複数のレコードにわたって反復処理を行うための仕組みです。カーソルは、反復処理を行うインデックスやオブジェクトストアがどれかを示す source を持ちます。またレンジ内の位置や、レコードキーの順序について増加方向に移動しているか減少方向に移動しているかの情報も持ちます。カーソルのリファレンス文書として、`IDBCursor` をご覧ください。

#### キーレンジ

キーとして使用する、何らかのデータ型の連続的な区間です。キーまたはキーレンジを使用して、オブジェクトストアやインデックスからレコードを取り出すことができます。下限または上限を使用して、レンジを制限またはフィルターリングできます。例えばキーが x から y の間であるすべての値に対して、反復処理を行うことができます。

キーレンジのリファレンス文書として、`IDBKeyRange` をご覧ください。

#### スコープ

トランザクションの適用先であるオブジェクトストアやインデックスのセットです。読み取りのみのトランザクションのスコープは、同時に重ね合ったり実行することができます。一方、書き込みトランザクションのスコープは重ね合うことができません。同時に同一のスコープで複数のトランザクションを開始することはできますが、それらはキューに収められ、順番に実行されます。

次のステップ
------

IndexedDB の主な特徴と主要な用語を理解できたら、より具体的な作業に入ることができます。API の使い方のチュートリアルについては、IndexedDB の使用をご覧ください。

関連情報
----

* Indexed Database API 仕様書
* IndexedDB API リファレンス
* IndexedDB の使用
* IndexedDB — The Store in Your Browser