IndexedDBにはまってます。(その19)

IndexedDBにはまってます。(その18)でプライマリキー(主キー)によるカーソル運用までを紹介いたしました。

本投稿からインデックスキー(補キー)の登録と利用について紹介します。

ところで、以前IndexedDBにはまってます。(その9)で且つ仕切り直して(その14)にもお断りしましたが、プライマリキー(主キー)を「インラインキー」モードにして紹介を続けてきました。

説明が煩雑になるのを嫌ってのことですが、個人の開発においては「アウトオブラインキー」モードの検証も続けております。

キーの指定がなくて、値に制約があること以外には特に問題はありません。ある意味?楽ではあります。

ただし開発コードの中では、「インラインキー」と「アウトオブラインキー」を判別する必要があり、処理の中で、オブジェクトストアの指定状況を取得して判定してます。

今までのコードで、データベースを開いた成功イベントの発火受け取り枠の冒頭に、つぎのようにトランザクションを稼働させて、完遂、成功、中断イベントのイベントリスナーを記述したあとで、トランザクションからオブジェクトストアを取得する記述があります。

tx.addEventListener('complete', (event)=>{console.log(`トランザクションが完遂しました。`);}, false);
tx.addEventListener('error', (event)=>{console.error(`トランザクションでエラーが発生しました。`);}, false);
tx.addEventListener('abort', (event)=>{console.error(`トランザクションが中断しました。`);}, false);
const objectStore = tx.objectStore('Store_name');

記述したこの定数objectStoreのプロパティからオブジェクトストアの設定を取得できます。主だったものを紹介します。

  • 「objectStore.name」であればオブジェクトストアの名前です。(その3)で確認してください。
  • 「objectStore.keyPath」であればプライマリキー(主キー)の指定(複数であれば配列)でインラインキーモード、そしてnullであれはアウトオブラインキーモードです。(その8)(その9)で確認してください。
  • 「objectStore.autoIncrement」であればキージェネレータの真偽値の指定です。同じく、(その8)(その9)で確認してください。
  • 「objectStore.indexNames」であればインデッックスキー(補キー)の指定一覧(DOMStringList)です。

最後のインデッックスキー(補キー)については、本稿から紹介が始まります。(ここまでは閑話ではなくて掴みだったのですー。)

プライマリキー(主キー)はオブジェクトストア内で一意であって、個々のレコードをユニークに運用するために置かれています。

でも、インデックスキー(補キー)は一意にもそうでなくも指定ができて、さらにマルチエントリーの指定も可能です。

ただし、基本的な利用概念はプライマリキー(主キー)と同様です。(基本ですよ。)

細かいことはいろいろとありますが、プライマリキー(主キー)で一本筋を通しておいて、インデックスキー(補キー)で多様にレコードを運用できるようになると認識してます。

インデックスキー(補キー)の登録は、上記の成功イベントの発火受け取り枠ではなく、データベースやオブジェクトストアを生成するアップグレードイベントの発火受け取り枠で行われます。

最初の一歩あたり、IndexedDBにはまってます。(その3)に戻った気分ですが、その枠内でオブジェクトストアの生成が行われ、プライマリキー(主キー)が登録された後に、インデックスキー(補キー)を登録(複数)します。

これ、基本の登録方法です。

ほかに、必要に応じてオブジェクトストアにインデックスキー(補キー)を追加登録したり削除することもできます。

登録の前にインデックスキー(補キー)の指定内容について紹介します。

検証用に、複数のインデックスキー(補キー)を登録するための指定項目を配列で準備します。

つぎのコードをWebブラウザ(Google Chromeを利用)のコンソールにコピー&ペーストし、エンターキーで登録します。

const storeIndex = [
	{name:'indexEn', path:'index.en', unique:false, multi:false},
	{name:'indexSn', path:'index.sn', unique:false, multi:false},
	{name:'id', path:'id', unique:true, multi:false}
];
コンソールに登録されたインデックスキー(補キー)指定用の配列

指定項目の説明をします。

‘storeIndex[].name’

文字列でインデックス(補キー)の任意の名前です。あくまでも名前にすぎませんので、オブジェクトストアの中でユニークに判別が付けば良さそうです。確認まで、空文字(”)で登録できます。でも一つまでです。対象が不確かになると思うので、個人的にはお勧めしません。

例えば、最初の要素では’index.en’の参照パスに対して判りやすいように’indexEn’と名前をつけてます。(名前も同じく’index.en’とできますが判別しやすいようにラクダ表記にしてます。)

‘storeIndex[].path’

インデックス(補キー)のKeyPath(参照パスの文字列)文字列もしくはその配列です。「なんでやねん。」と思うのですが、これも空文字(”)を指定できます。でも、ブラウザによるのかもしれませんが、正常に動作してくれない様子です。

‘index.en’はプライマリキー(主キー)の片方のパスと気が付かれたかもしれません。そーなんです。重複して登録ができるのです。ただし、プライマリキー(主キー)の一意と矛盾する運用はできないと推測します。(馬鹿馬鹿しいので検証しません。)

IndexedDBにはまってます。(その8)で触れないようにしてますが、つぎのようにプライマリキー(主キー)を空文字(”)指定して、且つ、キージェネレータをオンにした「インラインキー」モード指定のオプションで、オブジェクトストアが作れてしまいます。

しかしながら、空文字(”)による参照ができるわけもなく、意味のない徒労に終わります。その経験から、空文字(”)指定については基本的にアウトオブ眼中です。

const Store_option = {keyPath:'', autoIncrement:false};

‘storeIndex[].unique’

インデックスキー(補キー)をプライマリキー(主キー)のように一意に運用するための真偽値です。真(true)であれは重複を許さない一意になります。

‘storeIndex[].multi’

「マルチエントリー」の正否を指定する真偽値です。インデックス(補キー)の値が配列である場合に効果があります。真(ture)であれば、検索時に一つでも合致があれば対象とみなされるようになります。(すみません。これ未検証です。)

それでは、登録した指定配列を使ってオブジェクトストアの生成と共にインデックスキー(補キー)を登録します。コードの説明は後です。

つぎのコードをWebブラウザのコンソールにコピー&ペーストし、エンターキーで実行します。

const openRequest = indexedDB.open('DB_name');
openRequest.addEventListener('upgradeneeded', (event) => {
	const db = event.target.result;
	//const tx = event.target.transaction;
	console.log(`UPGRADE_DB_VER_>${db.version}_OLD_>${event.oldVersion}_NEW_>${event.newVersion}`);
	//オブジェクトストアのオプション指定を変数に代入する。
	const Store_option = {keyPath:['index.en', 'index.sn'], autoIncrement:false};
	try {
		//オブジェクトストア操作箇所
		//Store_optionをオブジェクトストアの生成メソッドのオプションパラメータとする。
		const objectStore = db.createObjectStore('Store_name', Store_option);

		//インデックスキー(補キー)を登録する。
		storeIndex.forEach((v, i, a) => {
			try {
				objectStore.createIndex(v.name, v.path, {unique:v.unique, multiEntry:v.multi});
			} catch(e) {
				console.error(`インデックスキー(補キー)の登録に例外が発生しました。`);
			}
		});

		objectStore.transaction.addEventListener('complete', (event)=>{
			if(event.target.objectStoreNames.contains('Store_name')) {
				console.log(`オブジェクトストアを生成しました。`);
			}
		}, false);
		objectStore.transaction.addEventListener('abort', (event)=>{console.error(`トランザクションが中断しました。`);}, false);
		objectStore.transaction.addEventListener('error', (event)=>{console.error(`トランザクションでエラーが発生しました。`);}, false);
	} catch(exception) {
		console.error(`例外が発生しました。_>${exception.message}`);
	}
}, false);
openRequest.addEventListener('success', (event)=>{
	const db = event.target.result;
	console.log(`データベースを開始しました。`);
	db.close();
	db.addEventListener('close', ()=>{console.log(`データベースが閉鎖されました。`);}, false);
}, false);
openRequest.addEventListener('error', (event)=>{console.error(`データベースの開始に失敗しました。`);}, false);
コンソール画面の入力と結果表示
オブジェクトストアにインデックスキー(補キー)が登録される。

オブジェクトストア’Store_name’の下に、インデックスキー(補キー)の名前を見ることができます。

それでは「インデックスキー(補キー)を登録する。」部分のコードを説明します。

//インデックスキー(補キー)を登録する。
storeIndex.forEach((v, i, a) => {
	try {
		objectStore.createIndex(v.name, v.path, {unique:v.unique, multiEntry:v.multi});
	} catch(e) {
		console.error(`インデックスキー(補キー)の登録に例外が発生しました。`);
	}
});

先に登録したstoreIndex配列をforEachメソッドを使って順次処理して、IDBObjectStoreのcreateIndexメソッドを使って登録します。

第一パラメータはインデックス名、第二はそのパス、第三は省略可能なオプションの連想配列で、一意とマルチエントリーの真偽値になります。判り?やすいように簡潔に記述しますね。

objectStore.createIndex(name, path, {unique, multiEntry});

オブジェクトストア生成時のインデックスキー(補キー)の登録でした。

次回は、オブジェクトストアにインデックスキー(補キー)を追加登録したり削除する方法を紹介できるでしょう。同じくデータベースのアップグレードイベントの発火受け取り枠内にて処理します。

そしてついに、IndexedDBにはまってます。(その2)からあって(その5)からコメントにして温存しておいた、四行目の「//const tx = event.target.transaction;」をコメントから解き放つ時が参りました。思った以上に長かったー。そかさ。