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

よって、今後はプライマリキー(主キー)を指定して「インラインキー」モードにて検証を進め、紹介をすすめていきます。(ちゃぶ台返し?のないことを祈ります。)

IndexedDBにはまってます。(その11)でラングオブジェクト(Range)を紹介したら、IndexedDBにはまってます。(その13)で「シンプルな操作とするため…アウトオブラインキーのモードに…」とか言って、「インラインキー」で進めるのをすっかり忘れてしまってました。思いっきり、ちゃぶ台返してました。(恥ずかしい。)

確かに(その9)で宣っていた通りです。(その13)なんてインラインキーモードにしたらプライマリキー(主キー)の値に配列が使えちゃうだけのことです。今回のdeleteメソッドと指定は同じです。

(訂正)

すみません、思いっきり間違えてました。

インラインキーモードにしても、それだけで値に配列が使えるわけではありません。さらにキージェネレータをオフにしないとだめです。

まったく何言ってんだか。でも、勘違いするほどに複雑ということですか…。

大きな違いと言えば、countとgetAll、getAllKeysメソッドはラングオブジェクトしきゃパラメータにしないこと、今回のgetとgetKey、deleteメソッドは、ラングオブジェクトと、プライマリキー(主キー)の値をまんまパラメータにできるところです。

というわけで、ハードルをガン上げしてプライマリキー(主キー)を配列しかも階層付きにして、getとgetKey、deleteメソッドパラメータにラングオブジェクトを指定していきます。

少しだけ複雑ですが、基本は同じです。シンプルな操作を(その13)で心掛けたのは良かったのかもしれません。(あの世の祖母の声が聞こえる「そかさ。」と、←これ湘南弁の女言葉です。)

押してまいります。

まずは(その13)の冒頭にある「オブジェクトストアStore_name生成」コードのストアオプションの記述をつぎのようにします。keyPathの指定は、IndexedDBにはまってます。(その10)で確認してください。

ついでに(その9)にある通り、キージェネレータはkeyPathが「単一キーワード」か「null」でしか運用できないので、autoIncrementの指定は偽になります。(真にすると例外が発生しますよね。)

const Store_option = {keyPath:['index.en', 'index.sn'], autoIncrement:false};

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

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);
		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の生成をアプリケーション画面で確認します。

アプリケーション画面のオブジェクトストア

つづいて(その13)の「オブジェクトストアStore_name生成」コードの次にある「配列」コードをコンソールにコピー&ペーストして配列オブジェクトを準備します。

コンソール画面で配列を確認

さらに(その13)のその下の「レコード格納」コード(トランザクションがreadwriteモードのやつです。)をコンソールにコピー&ペーストし…、おおっとストップ!

今回はインラインキーモードですから、addメソッドの第2パラメータに指定があると例外が発火してしまします。

つぎの記述の第2パラメータを削除します。

storeRequest = objectStore.add(records[i].data, records[i].key);

つぎの記述に変えます。

storeRequest = objectStore.add(records[i].data);

つぎになります。そして、エンターキーで実行します。

const openRequest = indexedDB.open('DB_name');
openRequest.addEventListener('success', (event)=>{
	const db = event.target.result;
	console.log(`データベースを開始しました。`);
	if(db.objectStoreNames.contains('Store_name')) {
		const tx = db.transaction('Store_name', 'readwrite');
		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');
		try {
			//レコード操作箇所
			let storeRequest;
			for(let i=0; i<records.length; i++) {
				storeRequest = objectStore.add(records[i].data);
			}
			storeRequest.addEventListener('success', (event)=>{console.log(`ストア内操作に成功しました。`);}, false);	
			storeRequest.addEventListener('error', (event)=>{console.error(`ストア内操作に失敗しました。`);}, false);
		} catch(exception) {
			console.error(`例外が発生しました。_>${exception.message}`);
		}
	} else console.error(`目的のオブジェクトストアはありませんでした。`);
	db.close();
	db.addEventListener('close', ()=>{console.log(`データベースが閉鎖されました。`);}, false);
}, false);
openRequest.addEventListener('error', (event)=>{console.error(`データベースの開始に失敗しました。`);}, false);
コンソール画面の入力と結果表示

アプリケーション画面でレコードが格納されていることを確認します。(えと、レコード配列の順番を変えても、IndexedDBはちゃんとソートして格納してくれます。)

オブジェクトストアに格納されたレコード

そして(その11)の「ラングオブジェクト生成メソッド」コードをコンソールにコピー&ペーストしてメソッドを登録します。

コンソール画面にメソッドを登録

ラングオブジェクト生成コードはとりあえずつぎのようにします。

const range = newRange({start:{v:[2, 'SN0001'], b:false}, end:{v:[5, 'SN0005'], b:false}});
objectStore.get([2, 'SN0001']);

でも、今回はラングオブジェクトをパラメータにします。

objectStore.get(range);

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

const openRequest = indexedDB.open('DB_name');
openRequest.addEventListener('success', (event)=>{
	const db = event.target.result;
	console.log(`データベースを開始しました。`);
	if(db.objectStoreNames.contains('Store_name')) {
		const tx = db.transaction('Store_name', 'readonly');
		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');
		//ラングオブジェクト生成
		const range = newRange({start:{v:[2, 'SN0001'], b:false}, end:{v:[5, 'SN0005'], b:false}});
		try {
			let storeRequest;
			//レコード操作箇所
			storeRequest = objectStore.get(range);
			storeRequest.addEventListener('success', (event)=>{
				console.log(`ストア内操作に成功しました。_>>> ${event.target.result}`);
				console.dir(event.target.result);
			}, false);	
			storeRequest.addEventListener('error', (event)=>{console.error(`ストア内操作に失敗しました。`);}, false);
		} catch(exception) {
			console.error(`例外が発生しました。_>${exception.message}`);
		}
	} else console.error(`目的のオブジェクトストアはありませんでした。`);
	db.close();
	db.addEventListener('close', ()=>{console.log(`データベースが閉鎖されました。`);}, false);
}, false);
openRequest.addEventListener('error', (event)=>{console.error(`データベースの開始に失敗しました。`);}, false);
コンソール画面の入力と結果表示

おや?ラングオブジェクトでちゃんと領域が確保されているにも関わらず、返ってきたレコードは一つだけです。

疑問を覚えますよね。試しに(その13)で紹介したgetAllメソッドを使ってみます。(これで前回のちゃぶ台返しを解消できます。)

つぎのように記述を差し替えて変えます。

storeRequest = objectStore.getAll(range);
コンソール画面の入力と結果表示

ちゃんとレコード4件が返ってきましたね。

そうなんです。getメソッドは兎にも角にもレコード1件しか取得できないのです。ラングオブジェクトの領域があったら最初の1レコードだけです。

これ、getKeyメソッドも同じです。トランザクションの中で繰り返し処理も受け付けてくれません。

putとadd、そしてdeleteメソッドは、繰り返し処理してくれます。addメソッドをputやdeleteメソッドに変更して操作できます。(deleteメソッドは、主キーの配列を準備しないとダメですよ。)

そして、ラングオブジェクトの利用において、deleteメソッドは先のgetAllメソッドと同じく動作します。

次のように変更すれば、レコードが消滅します。

storeRequest = objectStore. delete(range);

でも、ちょっとまっったあー。

レコードの操作になりますから、トランザクションの’readonly’を’readwrite’に変更しましょう。

const tx = db.transaction('Store_name', 'readwrite');
コンソール画面の入力と結果表示

アプリケーション画面で該当レコードの消滅が確認できます。そうなんです、deleteメソッドはラングオブジェクトの範囲を削除できます。

オブジェクトストアで減少した格納レコード

(追記)

deleteメソッドも「objectStore.delete([2, ‘SN0001’]);」の様にパラメータをキー値で指定できます。

やはり、get やgetKeyメソッドと違って、キー値を配列で準備してfor文などで回して、個別にdeleteメソッドのパラメータに割り当ててやれば複数のレコードを削除できます。

冒頭ゴタゴタしてそこそこ長くなりましたが、いろいろと盛り込めた投稿になりました。(読まされる方はたまったものではないかもしれません。)

ラングオブジェクトはプライマリキー(主キー)だけでなくインデックス(補キー)でも有効で、さらにカーソル運用でも利用されます。

(追記)

getgetKeydeleteメソッド(注意:これらはIDBObjectStoreのインスタンスメソッドです。)では、パラメータを「get(null)」とか「get()」にして実行すると例外が発生します。

よって、ラングオブジェクトがnullになるケースは回避しなくてはなりません。また、キーをパラメータにする場合も値がnullにならない様にします。

いよいよもって頭がこんがらがってきます。

それは、自分のためにも、できる限り回避したいものと考え…。結果、つぎはカーソルのお話をするのがよかろうとなってます。そかさ。