IndexedDBにはまってます。(その15)と(その17)につづきカーソルによるデータベースの利用です。
カーソルには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の基本概念は網羅できたと、ほととぎす。
ちょっとだけ感慨深いものがあります。まだ大御所様が残ってますけどね。そかさ。