IndexedDB は、ユーザのブラウザ内にデータを永続的に保存する手段です。ネットワークの状態にかかわらず高度な問い合わせ機能を持つ Web アプリケーションを作成できますので、オンラインとオフラインの両方で動作するアプリケーションになります。IndexedDB は大量のデータを保存するアプリケーション (例えばレンタル店の DVD カタログ) や、動作するために持続的なインターネット接続が不要なアプリケーション (例えばメールクライアント、To-Do リスト、メモ帳) で役に立ちます。
このドキュメントについて
この概論では、IndexedDB の本質的な概念や用語について論じます。これにより全体像を示すとともに、重要な概念を説明します。
以下の役に立つ項目があります:
- IndexedDB の用語について詳しく学ぶには、定義の章をご覧ください。
- API の使用方法に関する詳しいチュートリアルについては、IndexedDB を使用する をご覧ください。
- IndexedDB API のリファレンスドキュメントについては、メインページ IndexedDB API のサブページをご覧ください。IndexedDB で使用するオブジェクト型について記載しています。
- ブラウザがバックグラウンドでデータの保存を扱う方法について詳しくは、ブラウザのストレージ制限と削除基準をご覧ください。
IndexedDB の概要
IndexedDB では、"キー" でインデックス付けされたオブジェクトを保存および取り出すことができます。データベースに対して施したすべての変更は、トランザクションで発生します。たいていの Web ストレージ技術と同様に、IndexedDB も同一生成元ポリシーに従います。よって、保存済みデータは同一ドメイン内からアクセスできますが、異なるドメインにまたがってデータへアクセスすることはできません。
IndexedDB は、Web Workers を含むほとんどの状況で使用できる非同期 API です。以前は Web Workers で使用するための同期 API も含まれていましたが、Web コミュニティが無関心であったために削除されました。
IndexedDB と競合する仕様である WebSQL データベースがありましたが、W3C は 2010 年 11 月 18 日に非推奨にしました。IndexedDB も WebSQL もデータ保存の技術ですが、提供する機能が異なります。WebSQL データベースはリレーショナルデータベースシステムであるのに対して、IndexedDB はインデックス付きのテーブルシステムです。
重要な概念
ほかのタイプのデータベースを扱った経験から想定していることがあるなら、IndexedDB を扱うときにはそれを捨てましょう。そして、以下の重要な概念を覚えておいてください:
-
IndexedDB データベースはキーと値のペアを保存します。値は複雑な構造のオブジェクトにすることができます。また、キーはそれらのオブジェクトのプロパティにすることができます。分類された一覧表だけでなく、高速検索のためにオブジェクトの任意のプロパティを使用するインデックスを作成することができます。
-
IndexedDB はトランザクショナルデータベースモデルに基づいて構築されます。IndexedDB で行うことはすべて、トランザクション環境で発生します。IndexedDB API はインデックス、テーブル、カーソルなどを表す多くのオブジェクトを提供していますが、それらは個々のトランザクションに結びつけられています。よって、トランザクションの外部でコマンドを実行したりカーソルを開くことはできません。トランザクションには明確な存続期間がありますので、完了後にトランザクションを使おうとすると例外が発生します。また、トランザクションは自動コミットであり、手動コミットはできません。
このトランザクションモデルは、ユーザが同時に 2 つの異なるタブを使用して Web アプリのインスタンスを 2 つ開いた場合に何が起きるかを考慮すると、とても役に立ちます。トランザクショナルな操作がなければ、2 つのインスタンスでお互いの変更点が干渉するでしょう。データベースのトランザクションについて詳しくない場合は、Wikipedia の Database transaction の記事 (日本語版) をご覧ください。また、定義の章のトランザクションもご覧ください。
-
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 の Object database の記事 (日本語版) をご覧ください。
-
IndexedDB は Structured Query Language (SQL) を使用しません。IndexedDB はカーソルを生成するインデックスでクエリーを使用しており、結果セットで反復処理を行うために使用します。NoSQL システムについて詳しくない場合は、Wikipedia の NoSQL の記事 (日本語版) をご覧ください。
-
IndexedDB は同一生成元ポリシーに従います。生成元は、スクリプトを実行したドメイン、アプリケーション層プロトコル、ドキュメントの URL のポートです。それぞれの生成元が、自身に関連付けられたデータベースセットを持ちます。すべてのデータベースが、どの生成元に所属するかを識別する名称を持ちます。
IndexedDB に与えられたセキュリティ境界は、アプリケーションが別の生成元のデータにアクセスできないようにします。例えば https://www.example.com/app/ のアプリやページは、同一生成元である https://www.example.com/dir/ からデータを取り出すことができます。しかし、生成元が異なる https://www.example.com:8080/dir/ (ポートが異なる) や https://www.example.com/dir/ (プロトコルが異なる) からデータを取り出すことはできません。
注記: サードパーティの window コンテンツ (例えば コンテンツ) は、ブラウザでサードパーティ Cookie を常に拒否するように設定していない限り、自身が組み込まれた生成元の IndexedDB ストアにアクセスできます (バグ 1147821 をご覧ください)。
定義
この章では、IndexedDB API で使用する用語について定義および説明します。
データベース
- データベース (database)
- 主に 1 個以上のオブジェクトストアで構成される、情報のリポジトリです。それぞれのデータベースが以下のものを持ちます:
- 名称。これは特定の生成元に所属するデータベースを識別しており、データベースの存続期間を通じて不変です。名称は任意の文字列値です (空文字列を含む)。
-
現在のバージョン。始めにデータベースを生成したとき、バージョンは特に指定しない限り整数の 1 になります。それぞれのデータベースは、任意の時点でバージョンを 1 つだけ持ちます。
- 永続性 (durable)
-
Firefox では、IndexedDB を永続性があるものとして使用します。つまり、読み書きトランザクションで
IDBTransaction.oncomplete
は、すべてのデータをディスク上に反映したことが保証された時点で発生します。Firefox 40 の時点で、IndexedDB トランザクションはパフォーマンスを向上させるために、永続性の保証を緩和していました (バグ 1112702 を参照)。これは IndexedDB をサポートする他のブラウザと同じ動作です。この場合、
complete
イベントは OS がデータの書き込みを指示した時点で発生しますが、実際にはデータがディスク上に反映されていない可能性があります。これによりイベントをより早く発生させられますが、データをディスク上に反映する前に OS のクラッシュや電源断が発生するとトランザクション全体を失う危険性が若干あります。このような破壊的な事象はまれですので、ほとんどの利用者は心配する必要がないでしょう。注記: Firefox では、何らかの理由 (例えば、後で再計算できない重要なデータを保存する) で永続性を保証したい場合は、実験的 (非標準) な
readwriteflush
モード (IDBDatabase.transaction
を参照) を使用してトランザクションを生成すると、complete
イベントを発生させる前にディスクへの反映を強制させることができます。これは現在実験的な扱いであり、about:config
でdom.indexedDB.experimental
をtrue
に設定した場合に限り使用できます。 - オブジェクトストア (object store)
-
データベースにデータを保存する仕組みです。オブジェクトストアはレコードを持続的に保持しており、これはキーと値のペアです。オブジェクトストア内のレコードは、キーによって昇順に整列して保存されています。
すべてのオブジェクトストアは、データベース内で一意な名称を持たなければなりません。オブジェクトストアは、任意でキージェネレータやキーパスを持つことができます。オブジェクトストアがキーパスを持つ場合は、インラインキーを使用します。それ以外の場合は、アウトオブラインキーを使用します。
オブジェクトストアのリファレンスドキュメントとして、IDBObjectStore または IDBObjectStoreSync をご覧ください。
- バージョン (version)
- 始めにデータベースを生成したとき、バージョンは整数の 1 になります。それぞれのデータベースは、一度に 1 つのバージョンを持ちます。一度に複数のバージョンを持つことはできません。バージョンを変更する唯一の方法は、現在のバージョンより大きなバージョンでデータベースを開くことです。これは
versionchange
トランザクションを開始して、upgradeneeded
イベントが発生します。データベースのスキーマを更新できる唯一の場所が、このイベントのハンドラ内です。 - 注記: この定義は直近の仕様書で説明されており、最新のブラウザのみ実装しています。古いブラウザは非推奨かつ削除済みの
IDBDatabase.setVersion()
メソッドを実装しています。 - データベース接続 (database connection)
- データベースを開くことで生成される操作です。データベースは同時に複数の接続を持つことができます。
- トランザクション (transaction)
-
特定のデータベースで行う、原子性を持つデータアクセスやデータ変更の操作のセットです。これは、データベース内のデータと対話する手段です。実際は、データベース内のデータの読み取りや変更はトランザクション内で実施しなければなりません。
書き込みトランザクションのスコープが重ならない限り、ひとつのデータベース接続で同時に複数のアクティブなトランザクションが存在できます。トランザクションのスコープは生成時に定義され、トランザクションがどのオブジェクトストアと対話できるかや、トランザクションの持続期間にわたって保持し続けるかを示します。よって例えば、データベース接続で
flyingMonkey
オブジェクトストアのみ対象とするスコープを持つ書き込みトランザクションがすでに存在するとき、unicornCentaur
オブジェクトストアやunicornPegasus
オブジェクトストアをスコープで持つ別のトランザクションを開始できます。読み取りトランザクションは、スコープが重なっていても複数実行できます。トランザクションは持続期間が短いものを除き、長時間のトランザクションがストレージ資源をロックする状況から解放するために、ブラウザが終了させることができます。トランザクションは中止させることができ、トランザクションによるデータベースの変更箇所はロールバックされます。また、開始するトランザクションや中止するトランザクションを待つ必要はありません。
トランザクションには
readwrite
、readonly
、versionchange
の 3 つのモードがあります。オブジェクトストアやインデックスの生成および削除は、versionchange
トランザクションを使用する場合に限り実行できます。トランザクションのタイプについて詳しくは、IndexedDB のリファレンスをご覧ください。すべての操作はトランザクション内で発生しますので、トランザクションは IndexedDB の重要な概念です。トランザクションについて、特にバージョニングとの関係については、IDBTransaction および関連ドキュメントをご覧ください。また同期 API については、IDBTransactionSync をご覧ください。
- リクエスト (request)
- データベースの読み書きを実施する操作です。すべてのリクエストは、ひとつの読み取りまたは書き込みの操作を表します。
- インデックス (index)
-
インデックスは参照先オブジェクトストア (referenced object store)から呼び出されて、別のオブジェクトストアのレコードを検索するための特別なオブジェクトストアです。インデックスは持続的なキーと値のストレージであり、インデックスのレコードの値は、参照先オブジェクトストアのレコードのキーです。インデックス内のレコードは、参照先オブジェクトストアでレコードが挿入、更新、削除されるたびに、自動的に収集されます。インデックス内の各レコードは参照先オブジェクトストア内のレコードをひとつだけ示すことができますが、複数のインデックスが同一のオブジェクトストアを参照することもできます。オブジェクトストアが変更されると、そのオブジェクトストアを参照するすべてのインデックスが自動的に更新されます。
代わりに、キーを使用してオブジェクトストア内のレコードを検索することもできます。
インデックスの使用法について詳しくは、IndexedDB を使用する をご覧ください。インデックスのリファレンスドキュメントとして、IDBKeyRange をご覧ください。
キーと値
- キー (key)
-
オブジェクトストアに保存された値は、このデータ値によって編成および取り出しされます。オブジェクトストアはキージェネレータ、キーパス、明示的に指定した値の、3 種類の生成源のいずれかからキーを得られます。キーは、自身の前にあるものより大きな数値を持つデータ型であることが必要です。オブジェクトストア内の各レコードはオブジェクトストア内で一意のキーを持たなければならず、オブジェクトストア内で複数のレコードが同じキーを持つことはできません。
キーは 文字列、date、浮動小数点数値、配列のいずれかの型を使用できます。配列では、キーは空の値から無限大までの範囲を使用できます。また、配列の中に配列を含めることができます。文字列または整数値のキーしか使用できないという条件はありません。
代わりに、インデックスを使用してオブジェクトストア内のレコードを検索することもできます。
- キージェネレータ (key generator)
- 指定した順序で新たなキーを生成する仕組みです。オブジェクトストアがキージェネレータを持たない場合は、保存するレコードのキーをアプリケーションが提供しなければなりません。ジェネレータはストア間で共有しません。これはむしろブラウザの実装の細部であり、Web 開発において実際にはキージェネレータの生成やアクセスは行いません。
- インラインキー (in-line key)
- 保存される値の一部として保存されるキーです。これはキーパスを使用して見つけます。インラインキーは、ジェネレータを使用して生成できます。キーが生成されると、キーパスを使用してキーを値の中に保存したり、キーとして使用したりすることができます。
- アウトオブラインキー (out-of-line key)
- 保存する値とは別に保存されるキーです。
- キーパス (key path)
- オブジェクトストアやインデックスのどこからブラウザがキーを取り出すべきかを定義します。有効なキーパスは空文字列、JavaScript の識別子、ピリオドで区切られた複数の JavaScript の識別子、あるいはそれらを収めた配列のいずれかを含むことができます。空白を含むことはできません。
- 値 (value)
-
それぞれのレコードは値を持っており、論理値、数値、文字列、date、オブジェクト、配列、正規表現、undefined、null を含む、JavaScript で表現可能なものをどれでも含むことができます。
オブジェクトまたは配列を保存する場合は、それらのプロパティや値もまた、有効な値をどれでも持つことができます。
レンジとスコープ
- スコープ (scope)
- トランザクションの適用先であるオブジェクトストアやインデックスのセットです。読み取りのみのトランザクションのスコープは、同時に重ね合ったり実行することができます。一方、書き込みトランザクションのスコープは重ね合うことができません。同時に同一のスコープで複数のトランザクションを開始することはできますが、それらはキューに収められ、順番に実行されます。
- カーソル (cursor)
- キーレンジに属する複数のレコードにわたって反復処理を行うための仕組みです。カーソルは、反復処理を行うインデックスやオブジェクトストアがどれかを示す source を持ちます。またレンジ内の位置や、レコードキーの順序について増加方向に移動しているか減少方向に移動しているかの情報も持ちます。カーソルのリファレンスドキュメントとして、IDBCursor や IDBCursorSync をご覧ください。
- キーレンジ (key range)
-
キーとして使用する、何らかのデータ型の連続的な区間です。キーまたはキーレンジを使用して、オブジェクトストアやインデックスからレコードを取り出すことができます。下限または上限を使用して、レンジを制限またはフィルタリングできます。例えばキーが x から y の間であるすべての値に対して、反復処理を行うことができます。
キーレンジのリファレンスドキュメントとして、IDBKeyRange をご覧ください。
制限
IndexedDB は、クライアントサイドのストレージが必要なほとんどのケースに対応します。しかし、以下のような一部のケースに対して設計されてはいません:
- 国際化対応の整列。あらゆる言語について、同じ方法で文字列を整列することはできないため、国際化対応の整列はサポートされていません。特定の国際化順序でデータをデータベースに保存することはできませんが、データベースから読み出したデータを自身で整列することはできます。ただし Firefox 43 より、実験的なフラグをを有効化することでロケール対応のソートが可能になりました (現在は Firefox 限定)。
- 同期処理。API は、サーバサイドのデータベースとの同期を考慮して設計されていません。クライアントサイドの IndexedDB とサーバサードのデータベースで同期するように、コードを記述しなければなりません。
- 全文検索。API に、SQL の
LIKE
に相当するものは存在しません。
加えて、以下のような状況でブラウザがデータベースを削除する場合があることを意識してください:
- ユーザが削除を要求した場合。Cookie、ブックマーク、保存されたパスワード、IndexedDB のデータを含む、Web サイト用に保存されたすべてのデータをユーザが削除できる設定が多くのブラウザに存在します。
- ブラウザがプライベートブラウジングモードである場合。"プライベートブラウジング" (Firefox) や "シークレット" (Chrome) といったモードを持つブラウザがあります。セッションの終了時に、ブラウザはすべてのデータベースを削除します。
- ディスクまたはクォータの容量制限に達した場合。
- データが破損した場合。
- 機能に対して互換性のない変更が施された場合。
正確な状況やブラウザの機能は時間とともに変化しますが、ブラウザベンダーの一般的な考え方は、可能な限りデータを維持するよう最大限に努力することです。
次のステップ
主要な概念を習得したら、より具体的なことを学べるようになります。API の使用方法に関するチュートリアルである、IndexedDB を使用する をご覧ください。
関連情報
仕様書
リファレンス
チュートリアル
- IndexedDB を使用する
- A simple TODO list using HTML5 IndexedDB註: この例は旧版の仕様に基づいており、最新のブラウザでは動作しません。削除された
setVersion()
メソッドを使用しています。
関連記事