В основе любой таблицы IndexedDB лежит первичный ключ (primary key), определяющий уникальность записей и обеспечивающий быстрый доступ к данным. В Dexie.js первичный ключ задаётся на уровне схемы таблицы и становится фундаментом всей модели хранения: он влияет на вставку, обновление, удаление и индексацию.
Первичный ключ в Dexie.js выполняет несколько критических функций:
В отличие от SQL-систем, IndexedDB и Dexie.js работают с объектными хранилищами, где первичный ключ может быть как простым, так и составным, а также автоматически генерируемым.
Автоинкрементный первичный ключ используется в случаях, когда
уникальный идентификатор должен генерироваться автоматически при
добавлении записи. В Dexie.js для этого применяется префикс
++ в описании схемы.
Пример определения:
const db = new Dexie('AppDB');
db.version(1).stores({
users: '++id, name, email'
});
Здесь id становится первичным ключом с
автоинкрементом.
IndexedDB поддерживает внутренний счётчик ключей, который:
Каждая новая запись без указанного ключа получает следующий доступный идентификатор:
await db.users.add({ name: 'Alice', email: 'a@mail.com' });
// id = 1
await db.users.add({ name: 'Bob', email: 'b@mail.com' });
// id = 2
Автоинкрементные ключи часто применяются в сценариях, где данные представляют собой сущности без внешнего идентификатора: локальные заметки, черновики, кэшированные объекты.
Явный первичный ключ предполагает, что значение идентификатора задаётся приложением. В этом случае Dexie.js не генерирует ключ автоматически, а использует значение, переданное в объекте.
Пример:
db.version(1).stores({
users: 'id, name, email'
});
Добавление записи:
await db.users.add({
id: 'user_123',
name: 'Alice',
email: 'a@mail.com'
});
Явные ключи могут быть:
Часто применяются UUID, slug или внешние идентификаторы API:
id: '550e8400-e29b-41d4-a716-446655440000'
Используются, когда идентификатор приходит из внешней системы:
id: 100245
Явные ключи становятся предпочтительными при построении офлайн-first архитектур, где локальная база должна соответствовать серверной модели данных.
Составной первичный ключ представляет собой комбинацию нескольких полей, объединённых в единый уникальный идентификатор. В Dexie.js он задаётся через квадратные скобки:
db.version(1).stores({
orders: '[userId+orderId], product, date'
});
Здесь первичный ключ состоит из двух частей:
userIdorderIdIndexedDB хранит составной ключ как упорядоченную последовательность значений. Это означает:
Пример вставки:
await db.orders.add({
userId: 1,
orderId: 42,
product: 'Keyboard'
});
Составные ключи позволяют эффективно группировать данные:
Пример запроса диапазона:
db.orders
.where('[userId+orderId]')
.between([1, 0], [1, 9999])
.toArray();
Это позволяет получить все заказы конкретного пользователя без дополнительных индексов.
В Dexie.js важно различать:
'[a+b]'
'a,b'
Пример:
db.version(1).stores({
logs: '[userId+timestamp], userId, timestamp'
});
Здесь:
[userId+timestamp] — первичный ключ;userId и timestamp — вспомогательные
индексы.Первичный ключ в IndexedDB является неизменяемым после вставки. Попытка обновления ключа фактически требует удаления и повторного добавления записи.
Любая попытка вставить запись с уже существующим ключом приводит к
ошибке ConstraintError.
Выбор ключа напрямую влияет на:
| Тип ключа | Генерация | Гибкость | Сценарии |
|---|---|---|---|
| Автоинкремент (++id) | автоматическая | низкая | локальные сущности |
| Явный (string/number) | внешняя | высокая | синхронизация, API |
| Составной ([a+b]) | комбинированная | средняя | иерархические данные |
db.version(1).stores({
notes: '++id, title, body'
});
Модель ориентирована на автономное создание данных без внешних идентификаторов.
db.version(1).stores({
users: 'id, name, updatedAt'
});
Используется идентификатор сервера, обеспечивающий консистентность между устройствами.
db.version(1).stores({
messages: '[chatId+messageId], chatId, createdAt'
});
Такая структура позволяет:
В Dexie.js первичный ключ перестаёт быть технической деталью и становится частью доменной модели. Его выбор определяет:
Проектирование ключей фактически задаёт архитектуру хранилища ещё до появления бизнес-логики.