我正在使用jquery-indexed-db jquery plugin作为一个简单的Web应用程序,它依赖于Indexeddb进行客户端存储。
我已成功修改the example code来存储和读取数据,但无法解决如何更新存储对象的问题。
这是数据库结构(我试图改变的对象商店以蓝色突出显示)
要开始,我想更新特定用户的“last_updated”值(keyPath:“username”,键值:“k1078511”,这是该objectStore的唯一标识符)
这是我正在使用的JavaScript代码:
//Get current time
var timestamp = new Date();
timestamp = Math.round(timestamp.getTime()/1000);
var updateObject={last_updated : timestamp};
// do update
updatingLocalStoreObject('userdata','k1078511',updateObject)
.done(function(){
console.log("did update");
loadFromObjectStore("userdata"); // reloads HTML view
})
.fail(function() {
console.log("failed update");
});
function updatingLocalStoreObject(table, keyValue, updateObject){
var deferredReady = $.Deferred();
var mode = 1; // READ_WRITE as per plugin API
// var mode = "readwrite"; // using IndexedDB Specification
$.indexedDB('AppTest').transaction(table, mode).then(function(){
console.log("Transaction 'on "+table+"' completed, all data updated");
deferredReady.resolve();
}, function(err, e){
console.log("Transaction 'update "+table+"' NOT completed", err, e);
deferredReady.reject();
}, function(transaction){
var userdata = transaction.objectStore(table);
console.log('doing put transaction');
console.log(updateObject);
userdata.put(updateObject,keyValue);
});
return deferredReady.promise();
}
这将在控制台中返回以下内容(突出显示相关日志项):
'TypeError',因为错误消息对我来说似乎很奇怪。存储的javascript对象确实包含该索引的整数(last_updated:0)
经过一番搜索,我发现some people using this plugin正在使用 var mode =“readwrite”(根据IndexedDB规范)而不是严格遵守插件API。 (见上面代码中的评论。
根据控制台,这显然效果更好,因为它运行交易:
但实际上Indexeddb存储中没有任何变化,HTML视图是相同的,并且在浏览器的Resources工具中检查也没有显示数据库中的任何更改。
有人可以帮忙吗?我非常感谢一个有效的代码示例。
最终我希望能够使用更复杂的'updateObject'来调用我的函数,例如:
var updateObject={current_level: 5, passes: 7, last_updated : timestamp};
答案 0 :(得分:0)
首先,感谢Parashuram Narasimhan和IndexedDB Polyfill的作者Jquery-IndexedDB plugin回答我的问题,指出我的问题test-suites page ,特别是“ObjectStore update”测试功能。
对于那些想直接回答我最初问题的人,滚动到最后。但是,如果我解释如何找到一个有效的解决方案,我认为它可能会帮助其他人(像我一样学习)......
首先,我将测试套件代码剥离为一个可以由html锚点触发的简单方法。 (请注意,我最初感到困惑的是,原始的“ObjectStore update”测试函数在更新之前首先“添加”了一个对象。也许该函数更好地命名为“ObjectStore创建数据然后更新”?)
无论如何,这是我重新编写的版本中的相关代码(其中'111'是我想要更新的密钥,并且要进行的更改在函数中是硬编码的):
$("a#modify").click(function(e) {
ObjectStoreUpdate(DB.OBJECT_STORE_1,111);
e.preventDefault();
});
function ObjectStoreUpdate(table,key){
// get object by key (return a reference to it)
$.indexedDB(DB.NAME).objectStore(table).get(key).then(function(obj){
console.log("Got the object to be updated:", obj);
// make changes
obj["String"] = "Resampled " + new Date();
obj["Boolean"] = !obj["Boolean"]; // toggle true/false
obj["modified"] = Math.floor((Math.random()*100)+1); // random number 1 to 100
var newVal = obj;
$.indexedDB(DB.NAME).objectStore(table).put(newVal, key).then(function(){
$.indexedDB(DB.NAME).objectStore(table).get(key).then(function(val){
console.log("Got back the updated values:", val);
}, function(err, e){
console.log("Could not get back updated data");
});
}, function(err, es){
console.log("Could not update data");
});
}, function(err, e){
console.log("Could not select by key:", key);
});
};
请注意,与我的初始尝试不同(在我的问题中),它不再使用详细的事务语法,而是使用the plugin api提供的速记。
从那里实现这个并重写原始代码并不太困难(同样,更改后的属性'last_updated'只是硬编码):
updatingLocalStoreObject(table, key){
var deferredReady = $.Deferred();
// get object by key (return a reference to it)
$.indexedDB(_DBNAME).objectStore(table).get(key).then(function(obj){
console.log("Got the object to be updated:", obj);
// make changes
var timestamp = new Date();
timestamp = Math.round(timestamp.getTime()/1000);
obj["last_updated"] = timestamp;
var newVal = obj;
$.indexedDB(_DBNAME).objectStore(table).put(newVal, key).then(function(){
$.indexedDB(_DBNAME).objectStore(table).get(key).then(function(val){
console.log("Got back the updated values:", val);
deferredReady.resolve();
}, function(err, e){
console.log("Could not get back updated data", err, e);
deferredReady.reject();
});
}, function(err, es){
console.log("Could not update data", err, es);
deferredReady.reject();
});
}, function(err, e){
console.log("Could not select by key:", key);
deferredReady.reject();
});
return deferredReady.promise();
}
不幸的是,这不起作用。与重构的测试套件版本不同,我在控制台中收到错误:
无法更新数据: “对象存储使用内联键,并提供了关键参数。”
原来有问题的一行是'put'请求:
$.indexedDB(_DBNAME).objectStore(table).put(newVal, key).then(function(){
StackOverflow上的这个issue has been asked before和注释指向a useful resource如何以及何时在indexeddb中使用键
所以解决方案(对于我的数据库结构,使用KeyPath)只是省略了密钥:
$.indexedDB(_DBNAME).objectStore(table).put(newVal).then(function(){
最后,这是完整的工作代码,它通过支持复杂的“updateObject”来回答原始问题的两个部分:
//Get current time
var timestamp = new Date();
timestamp = Math.round(timestamp.getTime()/1000);
var updateObject={current_level:999, pass:11, last_updated : timestamp};
//do update
updatingLocalStoreObject('userdata','k1078511',updateObject)
.done(function(){
console.log("did update");
loadFromObjectStore("userdata"); // reloads HTML view
})
.fail(function() {
console.log("failed update");
});
function updatingLocalStoreObject(table,key, updateObject){
var deferredReady = $.Deferred();
// get object by key (return a reference to it)
$.indexedDB(_DBNAME).objectStore(table).get(key).then(function(obj){
console.log("Got the object to be updated:", obj);
// make changes
$.each(updateObject, function(updateKey,updateVal){
if (updateKey in obj){
obj[updateKey] = updateVal;
}
});
var newVal = obj;
$.indexedDB(_DBNAME).objectStore(table).put(newVal).then(function(){
$.indexedDB(_DBNAME).objectStore(table).get(key).then(function(val){
console.log("Got back the updated values:", val);
deferredReady.resolve();
}, function(err, e){
console.log("Could not get back updated data", err, e);
deferredReady.reject();
});
}, function(err, es){
console.log("Could not update data", err, es);
deferredReady.reject();
});
}, function(err, e){
console.log("Could not select by key:", key);
deferredReady.reject();
});
return deferredReady.promise();
}
最后的一些想法......首先在选定的keyPath上获取对整个对象的引用,然后在我定义的'updateObject'中对属性进行更改 - 本质上它遍历整个indexxeddb对象并且只更新那些匹配的属性,然后更新整个对象) - 这对小对象/数据集很好,但如果存在大量属性和数据则效率低下。我错过了什么吗?有没有办法直接改变一个属性?