Search code examples
indexeddbdexie

Fully dynamic index specification in DexieJS and IndexedDB


For my application I would like the user to be able to specify the indexed columns for a table.

I recognize that in order to do this I need to close the database, specify a new version, and then open it again. The minimize the impact of doing this I'm putting this custom part of the application in its own database.

Here is an example of how I am doing this.

import Dexie from 'dexie';

const db = new Dexie("CustomPeople");

db.version(1).stores({
  people: '++id'
});

function testColumnAdd() {

  modifyColumns(['firstName'])
    .then(() => {

      db.people.add({
        firstName: 'John'
      });

    })
    .then(() => {

      return modifyColumns(['firstName', 'lastName']);

    })
    .then(() => {

      db.people.add({
        firstName: 'John',
        lastName: 'Smith'
      });

    });

}

function modifyColumns(columns) {

  return db.open()
    .then(() => {

      const originalVersion = db.verno;

      console.log('original version: ' + originalVersion);

      db.close();

      return originalVersion;

    })
    .then((originalVersion) => {

      const newVersion = originalVersion + 1;

      console.log('adding columns ' + columns.join(','));

      console.log('new version version ' + newVersion);

      db.version(newVersion).stores({
        leads: '++id,' + columns.join(',')
      });

      return db.open();

    });

}

This appears to work fine each time that testColumnAdd() is called.

However, after page reload the first trigger of testColumnAdd() gives the following exception.

Unhandled rejection: VersionError: The requested version (10) is less than the existing version (70).

This definitely makes sense given all Dexie can see initially is version 1. Is there a way that I can read the current version and use it?

Is there a better way to approach user defined indexes in general?


Solution

  • I'm not sure if this was the intent of Dexie originally, but I found another way around the initialization of an existing database.

    I needed to store the definition of the columns in a separate database for other reasons. When I load the existing database I just build the correct version based on that metadata.

    const dbName = 'CustomPeople';
    
    const exists = await Dexie.exists(dbName);
    
    if (exists) {
    
        var db = new Dexie(dbName);
    
        const dynamicDB = await db.open();
    
        const existingVersionNumber = dynamicDB.verno;
    
        const columns = await ExternalDBService.getColumns();
    
        db.close();
    
        db = new Dexie(dbName);
    
        db.version(existingVersionNumber).stores({
            People: columns
        });
    
        return db.open();
    
    } else {
    
        db = new Dexie(dbName);
    
        db.version(1).stores({
            People: []
        });
    
        db.open();
    
    }