indexedDb-使用两个独立的密钥(本地和服务器ID/联机和脱机)进行保存/更新

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

本文关键字:服务器 ID 联机 更新 保存 脱机 两个 独立 indexedDb- 密钥      更新时间:2023-09-26

我想写一个Angular模块,它本地保存数据(使用IndexedDB),并通过RESTful服务将数据与服务器数据同步。

我已经为此构建了一个基本的基础,可以将数据获取到服务器并将其放入IndexedDB中。

因为web应用程序也必须在脱机模式下运行,所以我选择为对象存储使用两个键。自动递增的本地ID和服务器上条目的ID。

服务器不知道本地ID,如果无法将本地数据条目传输到服务器,则本地数据条目可能不知道服务器ID

当我为服务器ID定义一个唯一索引时,如果服务器ID已经存在并且更新过程停止,则本地条目不会得到更新。

有没有办法通过IDB API直接做到这一点?

我发现了一个类似的问题,但使用一个简单的游标和游标更新解决方案,就不可能将新数据从服务器插入到本地数据库:Indexeddb-通过索引键更新记录

我找到了一种方法。我将处理本地数据(带有本地id)和服务器数据(没有本地id,因为服务器不知道)。

然后,我将使用IDBObjectStore.put更新本地数据,并检查来自服务器的数据(服务器id已设置,但没有本地id)是否已通过单独的IDBIndex.openCursor调用保存在本地。如果找到具有给定serverId的本地数据,则会添加一个带有put IDBObjectStore.put的新条目,如果找到该条目,则会使用服务器数据更新该条目上的光标(旧的localId将被保留)。

// 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
        };
      }
    };
  })();
}

可能有一个更优雅的解决方案,但这是我想出的第一个,有效的。如果有任何改进或更好的解决方案,我将不胜感激。