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

カーソルにはputやaddメソッドはなくて、その代わり?にupdateメソッドがあります。

試してみたところ、putメソッドのようにごっそり差し替えることができました(今現在)が、レコード内で個別に値を更新できるのが特徴です。

なお、updateメソッドを使ってプライマリキー(主キー)は個別に変更できません。ごっそり差し替えても同じはずです。一意であるが故にロックが掛かっているのかもしれませんが、そこまで検証してません。

今回も全てのコピペ用コードをテキストファイル「code_IndexedDB_18.txt.zip」にまとめてアップロードしました。ご利用ください。

オブジェクトストアに格納されたレコード
コンソールにラングオブジェクト生成メソッドが登録されたところまで

コピペ用コードファイルがあるのをいいことに、説明を端折ります。

データベース作ってストアオブジェクトにいつものレコードを格納して、ラングオブジェクト生成メソッドを登録するところまでコンソールで作業をします。

そしたら、つぎのレコード用のアップデート用の配列(連想配列による)「upRecord」のコードをWebブラウザ(Google Chromeを利用)のコンソールにコピー&ペーストして登録します。

const upRecords = [
	{key:[1, 'SN0003'], data:{index:{en:1, sn:'SN0003'}, title:'鉛'}},
	{key:[2, 'SN0001'], data:{index:{en:2, sn:'SN0001'}, title:'金'}},
	{key:[3, 'SN0002'], data:{index:{en:3, sn:'SN0002'}, title:'銀'}},
	{key:[4, 'SN0004'], data:{index:{en:4, sn:'SN0004'}, title:'プラチナ'}},
	{key:[5, 'SN0005'], data:{index:{en:5, sn:'SN0005'}, title:'銅'}},
	{key:[6, 'SN0006'], data:{index:{en:6, sn:'SN0006'}, title:'アルミニウム'}}
];
コンソールに登録された更新用配列

upRecord配列は、データベースに格納したレコードの「title」項目を日本語に変更するために用意されてます。

元の配列と比較して、配列要素の「data.title」項目が日本語になっています。

また、「key」項目にそれぞれのレコードのプライマリキー(主キー)の実際の値が準備されていることに注目してください。

これ、今回の投稿を可能な限り短くするために準備した「ズル」です。

本来であれば、カーソルが繰り返されるたびにレコード(ここでは「cursor.value」)のプライマリキー(主キー)の値が、変更したいレコード(data)と同じかどうかを判定してから、更新項目を参照レコード(cursor.value)に代入してアップデートを仕掛けるべきです。

それをやるとなると、「cursor.source.keyPath」からパスを取得して、配列内の連想配列内の連想配列(data)からパスを使って値を取得してから、やっとプライマリキー(主キー)の値と比較する必要があります。ね、面倒でしょ?

私は作りましたよ。でも、説明に入れるとなると説明もコードも冗長に過ぎるので、省略するためにプライマリキー(主キー)の実際値を準備いたしました。

え?そんなに面倒でもない?

そうおっしゃる方もおられると思うのですが、続くインデックスキー(補キー)では処理階層が深くなる上にプライマリキー(主キー)とはパスの出所が違うのですよ。(うへー。)

となると、もう一度長々と説明が必要になってしまうのです。(それパス。)ということでご了承ください。

では、アップデートの紹介をいたします。確認まで、トランザクションの生成は’readwrite’モードですよ。

そしてやっぱり、updateメソッドもdeleteメソッドと同じくカーソルの移動はできません。advanceメソッドが必要になります。

(その17)と同じく(その15)の「カウント出力」行から「カウントインクリメント」までのカーソル処理ブロックが変更なります。

console.log(`カーソルの移動は_>${count}回めです。`);
console.log(`カーソルリクエストを参照_>${cursor.request}`);
//操作前のレコードを取得する。
r.push(JSON.parse(JSON.stringify(cursor.value)));
const upValue = cursor.value;
for(let i=0; i<upRecords.length; i++) {
	//更新データのkeyとプライマリキー(主キー)の値を比較する。
	if(indexedDB.cmp(upRecords[i].key, cursor.primaryKey)===0) {
		const ks = Object.keys(upRecords[i].data);
		for(let k of ks) {
			upValue[k] = upRecords[i].data[k];
		}
		break;
	}
}
let req = null;
try {
	req = cursor.update(upValue);
	req.addEventListener('success', (e)=>{
		console.log(`レコードの更新に成功しました。`);
	}, false);
	req.addEventListener('error', (e)=>{
		console. error(`レコードの更新に失敗しました。`);
	}, false);
} catch(e) {
	console.error(`カーソルによる操作に失敗しました。`);
	console.dir(e);
}
if(req!==null) {
	try {
		cursor.advance(cursorStep);
	} catch(e) {
		console.error(`カーソルの移動に失敗しました。`);
		console.dir(e);
	}
}
count++;

ではコードの説明をします。大きく追加されたのは次の部分です。

const upValue = cursor.value;
for(let i=0; i<upRecords.length; i++) {
	//更新データのkeyとプライマリキー(主キー)の値を比較する。
	if(indexedDB.cmp(upRecords[i].key, cursor.primaryKey)===0) {
		const ks = Object.keys(upRecords[i].data);
		for(let k of ks) {
			upValue[k] = upRecords[i].data[k];
		}
		break;
	}
}

最初に定数upValueにカーソルに入ってきたレコードの参照を代入しておきます。

つぎにそのレコードのプライマリキー(主キー)の値と、更新用配列upRecordsの中の「key」の値を比較します。

そして、ここ大切です。

比較にはIndexedDBにはまってます。(その16)のラングオブジェクト生成メソッドで使われていたindexedDB.cmpメソッドが使われます。

このメソッドのパラメータに二つの値を入れると比較してくれます。同じで値であった場合は「0」が返ってきます。

同じであった場合は、更新用配列の該当要素の「deta」オブジェクトのインデックスキー一覧を取得して、格納レコードの同じインデックスキーに更新データを代入します。

プライマリキー(主キー)は更新不可なので、ここで代入しちゃっても大丈夫みたいです。

その後はdeleteメソッドだったところをupdateメソッドに変更して、パラメータにupValueを入れるだけです。

req = cursor.update(upValue);

それでは、つぎのコードを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', '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');
		//ラングオブジェクト生成
		const range = newRange({start:{v:[2, 'SN0001'], b:false}, end:{v:[5, 'SN0005'], b:false}});
		//カーソル移動方向キーワード
		const direction = 'next';
		//カーソル移動ステップ(整数)
		const cursorStep = 1;
		let cursorRequest;
		try {
			//オブジェクトストアのカーソルを生成する。
			cursorRequest = objectStore.openCursor(range, direction);
		} catch(e) {
			console.error(`カーソルの生成に失敗しました。`);
		}
		if(cursorRequest!==null) {
			//カーソルによるオブジェクトストア操作箇所の始まり
			let r = new Array();
			let count = 0;
			cursorRequest.addEventListener('success', (e)=>{
				const cursor = e.target.result;
				if(cursor===null){
					console.log(`カーソルの移動が終了しました。`);
					if(count===0) console.error(`対象となるレコードがありませんでした。`);
					else {
						console.log(`カーソルの移動が終了しました。_全移動回数_>${count}`);
						console.dir(e.target.source);
						console.dir(r);
					}
				} else {
					console.log(`カーソルの移動は_>${count}回めです。`);
					console.log(`カーソルリクエストを参照_>${cursor.request}`);
					//操作前のレコードを取得する。
					r.push(JSON.parse(JSON.stringify(cursor.value)));
					const upValue = cursor.value;
					for(let i=0; i<upRecords.length; i++) {
						//更新データ(key)とプライマリキー(主キー)の値を比較する。
						if(indexedDB.cmp(upRecords[i].key, cursor.primaryKey)===0) {
							const ks = Object.keys(upRecords[i].data);
							for(let k of ks) {
								upValue[k] = upRecords[i].data[k];
							}
							break;
						}
					}
					let req = null;
					try {
						req = cursor.update(upValue);
						req.addEventListener('success', (e)=>{
							console.log(`レコードの更新に成功しました。`);
						}, false);
						req.addEventListener('error', (e)=>{
							console. error(`レコードの更新に失敗しました。`);
						}, false);
					} catch(e) {
						console.error(`カーソルによる操作に失敗しました。`);
						console.dir(e);
					}
					if(req!==null) {
						try {
							cursor.advance(cursorStep);
						} catch(e) {
							console.error(`カーソルの移動に失敗しました。`);
							console.dir(e);
						}
					}
					count++;
				}
			}, false);
			cursorRequest.addEventListener('error', (e)=>{
				console.error(`カーソルの移動の_>${count}回めを失敗しました。`);
			}, false);
			//カーソルによるオブジェクトストア操作箇所の終わり
		}
	} else console.error(`目的のオブジェクトストアはありませんでした。`);
	db.close();
	db.addEventListener('close', ()=>{console.log(`データベースが閉鎖されました。`);}, false);
}, false);
openRequest.addEventListener('error', (event)=>{console.error(`データベースの開始に失敗しました。`);}, false);
コンソール画面の入力と結果表示
オブジェクトストアの更新されたレコード

コンソールでバックアップ?的に取得したレコードとオブジェクトストアのレコードを比較します。項目「title」の金属の名前が日本語に変わりました。(よね?)

いよいよ、つぎからインデックスキー(補キー)にお話が進みます。(がんばろ。)

お、カーソル冬の陣が終わったあ。これでIndexedDBの基本概念は網羅できたと、ほととぎす。

ちょっとだけ感慨深いものがあります。まだ大御所様が残ってますけどね。そかさ。