Search code examples
javascriptasynchronousindexeddbydn-db

YDN-DB How to update a single field?


I've been trying to update a single field using db.put() but couldn't make it work properly. Every time I update a single field by a given ID, it deletes all other entries. Here is an example code:

 var schema   = {
            stores: [
                {
                    name: 'items',
                    keyPath: 'id'
                },
                {
                    name: 'config',
                    keyPath: 'id'
                }
            ]
        };

        var db = new ydn.db.Storage('initial', schema);
        var items = [{
            id: 1,
            itemId: 'GTA5',
            date:'03/25/2013',
            description:'Great game'
        }, {
            id: 2,
            itemId: 'Simcity',
            date:'12/01/2012',
            description:'Awesome gameplay'
        }];
        var config = {
            id: 1,
            currency: 'USD'
        };
        db.put('items', items);
        db.put('config', config);

var updateTable = function(){
        var req = $.when(db.count('items'),db.values('items'),db.get('config', 1));
        var disp = function(s) {
            var e = document.createElement('div');
            e.textContent = s;
            document.body.appendChild(e);
        };
        req.done(function(count,r,config) {
            var currency = config.currency;
            if(count > 0){
                var n = r.length;
                for (var i = 0; i < n; i++) {
                    var id    = r[i].id;
                    var itemId = r[i].itemId;
                    var date = r[i].date;
                    var description = r[i].description
                    disp('ID: '+id+' itemID: '+itemId+' Currency: '+currency+' Date: '+date+' Description: '+description);
                }
            }
        });
}

updateTable();

$('a').click(function(e){
    e.preventDefault();
    db.put('items',{id:2,description:'Borring'}).done(function(){
        updateTable();
    });
});

Here is a working example of whats happening JSFiddle. If you click the "change" link, the specified field is updated but all other fields are 'undefined'


Solution

  • Yeah, SimCity is boring now, but Tomorrow will bring excitement again.

    IndexedDB is essentially a key-document store. You have to read or write a record as a whole. It is NOT possible to update only certain field(s). Event if you want small update, you have to read and write back whole record.

    Reading and write back the whole record is OK, but there is an important consideration for consistency. When you write back, you must ensure that the the record you have was not modified by other thread. Even though javascript is single thread, since both read and write operations are asynchronous and each operation could have different database state. It seems extremely rare, but often happen. For example, when user click, nothing happen and then click again. These user interactions are queued and execute in parallel from async database perspective.

    A common technique is using single transaction for both operations. In YDN-DB, you can do in three ways.

    Using explicit transaction:

    db.run(function(tx_db) {
      tx_db.get('items', 2).done(function(item) {
        item.description = 'boring until Tomorrow';
        tx_db.put(item).done(function(k) {
          updateTable();
        }
      }
    }, ['items'], 'readwrite');
    

    Using an atomic database operation:

    var iter = ydn.db.ValueIterator.where('items', '=', 2);
    db.open(function(cursor) {
      var item = cursor.getValue();
      item.description = 'boring until Tomorrow';
      cursor.update(item);
    }, iter, 'readwrite');
    

    EDIT:

    Using query wrapper:

    db.from('items', '=', 2).patch({description: 'boring until Tomorrow'});