Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting object store already exists inside onupgradeneeded

My code is as follows (usually naming convention for the well-known objects):

var DBOpenRequest = window.indexedDB.open("messages", 6);
//...
DBOpenRequest.onupgradeneeded = function(event) { 
  console.log("Need to upgrade.");
  var db = event.target.result;
  console.log(db);

  db.onerror = function(event) {
     console.log("Error upgrading.");
  };

  // Create an objectStore for this database
  var objectStore = db.createObjectStore("messages", { keyPath: "id", autoIncrement: true });
    };

This ran fine for versions 3 and 4. When it came to version 5, I get the error:

Failed to execute 'createObjectStore' on 'IDBDatabase': An object store with the specified name already exists. at IDBOpenDBRequest.DBOpenRequest.onupgradeneeded

Isn't the createObjectStore operating on a new version of the database which is empty? How do I fix the error?

I happened to log the db object and the details are below:

enter image description here

I am curious why the version number is different in the summary line and when expanded.

like image 908
Old Geezer Avatar asked May 16 '17 13:05

Old Geezer


2 Answers

Isn't the createObjectStore operating on a new version of the database which is empty?

When you get upgradeneeded the database is in whatever state you left it in before. Since you don't know what versions of your code a user will have visited, you need to look at the event's oldVersion to find out what that was. The typical pattern is something like this:

var rq = indexedDB.open('db', 5);
rq.onupgradeneeded = function(e) {
  var db = rq.result;
  if (e.oldVersion < 1) {
    // do initial schema creation
    db.createObjectStore('users');
  }
  if (e.oldVersion < 2) {
    // do 1->2 upgrade
    var s = db.createObjectStore('better_users');
    s.createIndex('some_index', ...);
    db.deleteObjectStore('users'); // migrating data would be better
  }
  if (e.oldVersion < 3) {
    // do 2->3 upgrade
    rq.transaction.objectStore('better_users').createIndex('index2', ...);
  }
  if (e.oldVersion < 4) {
    // do 3->4 upgrade
    db.createObjectStore('messages', ...);
  }
  if (e.oldVersion < 5) {
    // do 4->5 upgrade
    // ...
  }
}

I am curious why the version number is different in the summary line and when expanded.

That one is subtle... I believe at the point where the 5 was logged the database had started the upgrade. But because an exception was thrown in the upgradeneeded handler the upgrade was aborted, and the version number was rolled back to 4 before the details were logged.

like image 103
Joshua Bell Avatar answered Nov 04 '22 09:11

Joshua Bell


The best way to upgrade the DB is checking if the store name is already there. In this example I'm using https://npmjs.com/idb

openDB('db-name', version, {
  upgrade(db, oldVersion, newVersion, transaction) {
    if(!db.objectStoreNames.contains('messages')) {
      db.createObjectStore('messages', { keyPath: "id", autoIncrement: true })
    }
  }
})

If you need to check if an indexName already exist, you can get the objectStore and check for the indexNames property if it contains the indexName you need.

openDB('db-name', version, {
  upgrade(db, oldVersion, newVersion, transaction) {
    const storeName = transaction.objectStore('storeName')
    if(!storeName.indexNames.contains('indexName')) {
        storeName.createIndex('indexName', 'propertyName', { unique: false });
    }
  }
})

Using indexDB API with indexNames and objectStoreNames to check if something is either there or not makes my code way more reliable and easy to maintain, it is also briefly mentioned on Working with IndexDB Using database versioning

like image 31
Felquis Avatar answered Nov 04 '22 10:11

Felquis