You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8.3 KiB

Dexie to SQLite Mapping Guide

Schema Mapping

Current Dexie Schema

// Current Dexie schema
const db = new Dexie('TimeSafariDB');

db.version(1).stores({
  accounts: 'did, publicKeyHex, createdAt, updatedAt',
  settings: 'key, value, updatedAt',
  contacts: 'id, did, name, createdAt, updatedAt'
});

New SQLite Schema

-- New SQLite schema
CREATE TABLE accounts (
  did TEXT PRIMARY KEY,
  public_key_hex TEXT NOT NULL,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL
);

CREATE TABLE settings (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL,
  updated_at INTEGER NOT NULL
);

CREATE TABLE contacts (
  id TEXT PRIMARY KEY,
  did TEXT NOT NULL,
  name TEXT,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL,
  FOREIGN KEY (did) REFERENCES accounts(did)
);

-- Indexes for performance
CREATE INDEX idx_accounts_created_at ON accounts(created_at);
CREATE INDEX idx_contacts_did ON contacts(did);
CREATE INDEX idx_settings_updated_at ON settings(updated_at);

Query Mapping

1. Account Operations

Get Account by DID

// Dexie
const account = await db.accounts.get(did);

// SQLite
const account = await db.selectOne(`
  SELECT * FROM accounts WHERE did = ?
`, [did]);

Get All Accounts

// Dexie
const accounts = await db.accounts.toArray();

// SQLite
const accounts = await db.selectAll(`
  SELECT * FROM accounts ORDER BY created_at DESC
`);

Add Account

// Dexie
await db.accounts.add({
  did,
  publicKeyHex,
  createdAt: Date.now(),
  updatedAt: Date.now()
});

// SQLite
await db.execute(`
  INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
  VALUES (?, ?, ?, ?)
`, [did, publicKeyHex, Date.now(), Date.now()]);

Update Account

// Dexie
await db.accounts.update(did, {
  publicKeyHex,
  updatedAt: Date.now()
});

// SQLite
await db.execute(`
  UPDATE accounts 
  SET public_key_hex = ?, updated_at = ?
  WHERE did = ?
`, [publicKeyHex, Date.now(), did]);

2. Settings Operations

Get Setting

// Dexie
const setting = await db.settings.get(key);

// SQLite
const setting = await db.selectOne(`
  SELECT * FROM settings WHERE key = ?
`, [key]);

Set Setting

// Dexie
await db.settings.put({
  key,
  value,
  updatedAt: Date.now()
});

// SQLite
await db.execute(`
  INSERT INTO settings (key, value, updated_at)
  VALUES (?, ?, ?)
  ON CONFLICT(key) DO UPDATE SET
    value = excluded.value,
    updated_at = excluded.updated_at
`, [key, value, Date.now()]);

3. Contact Operations

Get Contacts by Account

// Dexie
const contacts = await db.contacts
  .where('did')
  .equals(accountDid)
  .toArray();

// SQLite
const contacts = await db.selectAll(`
  SELECT * FROM contacts 
  WHERE did = ?
  ORDER BY created_at DESC
`, [accountDid]);

Add Contact

// Dexie
await db.contacts.add({
  id: generateId(),
  did: accountDid,
  name,
  createdAt: Date.now(),
  updatedAt: Date.now()
});

// SQLite
await db.execute(`
  INSERT INTO contacts (id, did, name, created_at, updated_at)
  VALUES (?, ?, ?, ?, ?)
`, [generateId(), accountDid, name, Date.now(), Date.now()]);

Transaction Mapping

Batch Operations

// Dexie
await db.transaction('rw', [db.accounts, db.contacts], async () => {
  await db.accounts.add(account);
  await db.contacts.bulkAdd(contacts);
});

// SQLite
await db.transaction(async (tx) => {
  await tx.execute(`
    INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
    VALUES (?, ?, ?, ?)
  `, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);

  for (const contact of contacts) {
    await tx.execute(`
      INSERT INTO contacts (id, did, name, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?)
    `, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]);
  }
});

Migration Helper Functions

1. Data Export (Dexie to JSON)

async function exportDexieData(): Promise<MigrationData> {
  const db = new Dexie('TimeSafariDB');
  
  return {
    accounts: await db.accounts.toArray(),
    settings: await db.settings.toArray(),
    contacts: await db.contacts.toArray(),
    metadata: {
      version: '1.0.0',
      timestamp: Date.now(),
      dexieVersion: Dexie.version
    }
  };
}

2. Data Import (JSON to SQLite)

async function importToSQLite(data: MigrationData): Promise<void> {
  const db = await getSQLiteConnection();
  
  await db.transaction(async (tx) => {
    // Import accounts
    for (const account of data.accounts) {
      await tx.execute(`
        INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
        VALUES (?, ?, ?, ?)
      `, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);
    }
    
    // Import settings
    for (const setting of data.settings) {
      await tx.execute(`
        INSERT INTO settings (key, value, updated_at)
        VALUES (?, ?, ?)
      `, [setting.key, setting.value, setting.updatedAt]);
    }
    
    // Import contacts
    for (const contact of data.contacts) {
      await tx.execute(`
        INSERT INTO contacts (id, did, name, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?)
      `, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]);
    }
  });
}

3. Verification

async function verifyMigration(dexieData: MigrationData): Promise<boolean> {
  const db = await getSQLiteConnection();
  
  // Verify account count
  const accountCount = await db.selectValue(
    'SELECT COUNT(*) FROM accounts'
  );
  if (accountCount !== dexieData.accounts.length) {
    return false;
  }
  
  // Verify settings count
  const settingsCount = await db.selectValue(
    'SELECT COUNT(*) FROM settings'
  );
  if (settingsCount !== dexieData.settings.length) {
    return false;
  }
  
  // Verify contacts count
  const contactsCount = await db.selectValue(
    'SELECT COUNT(*) FROM contacts'
  );
  if (contactsCount !== dexieData.contacts.length) {
    return false;
  }
  
  // Verify data integrity
  for (const account of dexieData.accounts) {
    const migratedAccount = await db.selectOne(
      'SELECT * FROM accounts WHERE did = ?',
      [account.did]
    );
    if (!migratedAccount || 
        migratedAccount.public_key_hex !== account.publicKeyHex) {
      return false;
    }
  }
  
  return true;
}

Performance Considerations

1. Indexing

  • Dexie automatically creates indexes based on the schema
  • SQLite requires explicit index creation
  • Added indexes for frequently queried fields

2. Batch Operations

  • Dexie has built-in bulk operations
  • SQLite uses transactions for batch operations
  • Consider chunking large datasets

3. Query Optimization

  • Dexie uses IndexedDB's native indexing
  • SQLite requires explicit query optimization
  • Use prepared statements for repeated queries

Error Handling

1. Common Errors

// Dexie errors
try {
  await db.accounts.add(account);
} catch (error) {
  if (error instanceof Dexie.ConstraintError) {
    // Handle duplicate key
  }
}

// SQLite errors
try {
  await db.execute(`
    INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
    VALUES (?, ?, ?, ?)
  `, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);
} catch (error) {
  if (error.code === 'SQLITE_CONSTRAINT') {
    // Handle duplicate key
  }
}

2. Transaction Recovery

// Dexie transaction
try {
  await db.transaction('rw', db.accounts, async () => {
    // Operations
  });
} catch (error) {
  // Dexie automatically rolls back
}

// SQLite transaction
const db = await getSQLiteConnection();
try {
  await db.transaction(async (tx) => {
    // Operations
  });
} catch (error) {
  // SQLite automatically rolls back
  await db.execute('ROLLBACK');
}

Migration Strategy

  1. Preparation

    • Export all Dexie data
    • Verify data integrity
    • Create SQLite schema
    • Setup indexes
  2. Migration

    • Import data in transactions
    • Verify each batch
    • Handle errors gracefully
    • Maintain backup
  3. Verification

    • Compare record counts
    • Verify data integrity
    • Test common queries
    • Validate relationships
  4. Cleanup

    • Remove Dexie database
    • Clear IndexedDB storage
    • Update application code
    • Remove old dependencies