Simon Jentsch
Simon Jentsch

Reputation: 717

indexedDb - saving/updating with two independent keys (local and server IDs / online and offline)

I want to write an Angular module that saves data locally (with IndexedDB) and sync that data with server data via a RESTful service.

I have build a rudimental base for that already that can get data to the server and put it into the IndexedDB.

Because the web application has to run in offline mode too, I have chosen to use two keys for the object stores. A local ID which is autoIncremented and the ID of the entry on the server.

The server isn't aware of the local ID and the local data entry might not know the server ID if it can't be transmitted to the server

When I define an unique index for the server ID, the local entries won't get updated if the server ID already exists and the update process stops.

Is there a way to do this directly with the IDB API?

I have found a similar problem, but with the an simple cursor and cursor.update solution, there is no possibility to insert new data from the server to the local DB: Indexeddb - Update record by index key

Upvotes: 0

Views: 537

Answers (1)

Simon Jentsch
Simon Jentsch

Reputation: 717

I have found a way to do this. I will handle the local data (with an local id) separately from the server data (no local id, because server doesn't know it).

Then i will update the local data with IDBObjectStore.put and check if the data that came from the server (server id is set, but no local id) is already saved locally with separate IDBIndex.openCursor calls. If local data was found with the given serverId, a new entry will be added with put IDBObjectStore.put, and if found the cursor on that entry will be updated with the server data (old localId will be carried over).

// var _db => connected database

// filter the data that came from the server (no local id defined but server id is)
var serverData = [];
for (var i = 0; i < data.length; i++) {
  if (data[i]._localId === undefined && data[i].serverId !== undefined && data[i].serverId !== null) {
    // remove the server data object and add it to the server data array
    serverData = serverData.concat(data.splice(i, 1)); 
    i--;
  }
}

var transaction = _db.transaction(_storageName, 'readwrite');

transaction.oncomplete = function() {
  // do something on completion
};

transaction.onerror = function(e) {
  // do something when an error occurs
};

var objectStore = transaction.objectStore(_storageName);

// Add local data to the database (no server id)
// local id can be existing or not (new entry)
for (var i = 0; i < data.length; i++) {
  objectStore.put(data[i]).onsuccess = function(e) {
    // do something when successfully added
  };
}

// Add data from the server to the database
var index = objectStore.index('serverId'); // server id index for searching
// go through all data from the server
for (var i = 0; i < serverData.length; i++) {
  (function(){
    var serverItem = serverData[i];
    // search for an existing entry in the local database
    var checkRequest = index.openCursor(IDBKeyRange.only(serverItem.serverId));
    checkRequest.onsuccess = function (e) {
      var cursor = e.target.result;
      // If item was not found in local indexedDB storage...
      if (cursor === null) {
        // add new item to storage
        this.source.objectStore.put(serverItem).onsuccess = function(e) {
          // do something when successfully added
        };

      // Item was found locally
      } else {
        var dbItem = cursor.value;
        // set local id of the added item to the one from the old local entry
        serverItem.localId = dbItem.localId; 
        // update found local entry with the one from the server
        cursor.update(serverItem).onsuccess = function(e) {
          // do something on success
        };
      }
    };
  })();
}

There could be a more elegant solution, but this one is the first one that I came up with, that worked. I would be thankful for any improvement or better solutions.

Upvotes: 1

Related Questions