Ключи на основе идентификаторов сущностей

Библиотека Idb-keyval предоставляет удобный интерфейс для работы с IndexedDB через простой ключ-значение API. Одним из мощных приёмов является использование идентификаторов сущностей в качестве ключей. Это позволяет хранить, получать и обновлять данные конкретных объектов без необходимости полного обхода базы данных.


Использование идентификаторов как ключей

В классическом подходе ключом может быть строка или число. При работе с сущностями, например, пользователями, товарами или сообщениями, логично использовать их уникальный идентификатор (например, id из базы данных) как ключ:

import { set, get, del } from 'idb-keyval';

const user = { id: 101, name: 'Алексей', email: 'alex@example.com' };

// Сохранение сущности с ключом на основе id
set(user.id, user).then(() => {
  console.log('Пользователь сохранен');
});

// Получение сущности по идентификатору
get(101).then(value => {
  console.log(value); // { id: 101, name: 'Алексей', email: 'alex@example.com' }
});

Преимущества подхода:

  • Прямой доступ к объекту по уникальному идентификатору.
  • Исключение дублирования данных: ключи уникальны.
  • Легкость интеграции с серверными API, где идентификаторы уже присутствуют.

Обновление и удаление сущностей

Idb-keyval позволяет быстро обновлять объекты по ключу, что особенно удобно для динамически изменяющихся сущностей:

get(101).then(user => {
  if (user) {
    user.email = 'alex_new@example.com';
    return set(user.id, user);
  }
}).then(() => console.log('Email обновлен'));

// Удаление сущности
del(101).then(() => console.log('Пользователь удален'));

Использование идентификаторов гарантирует, что обновление или удаление будет происходить только для конкретной сущности, без риска случайного затрагивания других объектов.


Массовая обработка сущностей

Для работы с множеством сущностей удобно использовать массивы идентификаторов и методы Promise.all:

const users = [
  { id: 201, name: 'Ирина' },
  { id: 202, name: 'Сергей' },
  { id: 203, name: 'Мария' }
];

// Сохранение нескольких сущностей одновременно
Promise.all(users.map(user => set(user.id, user)))
  .then(() => console.log('Все пользователи сохранены'));

// Получение нескольких сущностей
Promise.all([201, 202, 203].map(id => get(id)))
  .then(values => console.log(values));

Такой подход позволяет легко синхронизировать данные с сервером или локально кэшировать список объектов без необходимости хранить их в одном массиве.


Использование объектов в качестве сложных ключей

IndexedDB и, соответственно, Idb-keyval поддерживают сложные ключи, включая массивы. Это удобно для составных идентификаторов, например, при мультиключах:

const order = { orderId: 501, userId: 101, items: ['item1', 'item2'] };

// Составной ключ: [userId, orderId]
set([order.userId, order.orderId], order).then(() => console.log('Заказ сохранен'));

// Получение заказа по составному ключу
get([101, 501]).then(value => console.log(value));

Преимущества:

  • Уникальность ключей на уровне комбинаций полей.
  • Возможность структурирования данных для сложных отношений (например, пользователь → заказы → товары).
  • Простая фильтрация и поиск при дальнейшем переборе.

Практика и рекомендации

  1. Всегда использовать уникальные идентификаторы – это минимизирует ошибки при записи и извлечении данных.
  2. Для временных или кэшированных данных можно применять префиксы ключей: user:101, order:501. Это помогает избегать конфликтов между разными сущностями.
  3. Композитные ключи для отношений – когда сущность зависит от другой сущности (например, комментарий к посту), массив из [postId, commentId] дает прямой доступ без поиска.
  4. Не хранить массивы всех сущностей под одним ключом, если данные динамически изменяются. Это снижает производительность и усложняет обновление отдельных объектов.

Пример архитектуры с идентификаторами

Для приложения с пользователями и заказами структура локального хранения может выглядеть так:

// Пользователи
set('user:101', { id: 101, name: 'Алексей' });
set('user:102', { id: 102, name: 'Ирина' });

// Заказы
set(['order', 101, 501], { orderId: 501, userId: 101, items: ['item1'] });
set(['order', 101, 502], { orderId: 502, userId: 101, items: ['item2'] });

// Получение всех заказов пользователя 101
getAllOrdersForUser = async (userId) => {
  const orders = [];
  for (const orderId of [501, 502]) {
    const order = await get(['order', userId, orderId]);
    if (order) orders.push(order);
  }
  return orders;
};

Такой подход обеспечивает быстрый доступ к сущностям и поддерживает масштабируемость данных в IndexedDB через Idb-keyval.