无法使用jquery-indexeddb插件更新objectstore中的数据

时间:2014-01-30 16:01:26

标签: jquery jquery-plugins indexeddb

我正在使用jquery-indexed-db jquery plugin作为一个简单的Web应用程序,它依赖于Indexeddb进行客户端存储。

我已成功修改the example code来存储和读取数据,但无法解决如何更新存储对象的问题。

这是数据库结构(我试图改变的对象商店以蓝色突出显示) enter image description here

要开始,我想更新特定用户的“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();
}

这将在控制台中返回以下内容(突出显示相关日志项): enter image description here

'TypeError',因为错误消息对我来说似乎很奇怪。存储的javascript对象确实包含该索引的整数(last_updated:0)

经过一番搜索,我发现some people using this plugin正在使用 var mode =“readwrite”(根据IndexedDB规范)而不是严格遵守插件API。 (见上面代码中的评论。

根据控制台,这显然效果更好,因为它运行交易:

enter image description here

但实际上Indexeddb存储中没有任何变化,HTML视图是相同的,并且在浏览器的Resources工具中检查也没有显示数据库中的任何更改。

有人可以帮忙吗?我非常感谢一个有效的代码示例。

最终我希望能够使用更复杂的'updateObject'来调用我的函数,例如:

var updateObject={current_level: 5, passes: 7, last_updated : timestamp};

1 个答案:

答案 0 :(得分:0)

首先,感谢Parashuram NarasimhanIndexedDB 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对象并且只更新那些匹配的属性,然后更新整个对象) - 这对小对象/数据集很好,但如果存在大量属性和数据则效率低下。我错过了什么吗?有没有办法直接改变一个属性?