Реализация простого поиска по значениям

Idb-keyval — это минималистичная обёртка над IndexedDB, предназначенная для удобного хранения ключ-значение в браузере. Основной фокус библиотеки — простота использования: операции get, set, delete, clear и keys позволяют манипулировать данными без глубокого погружения в низкоуровневый API IndexedDB.

Для реализации поиска по значениям важно понимать, что стандартные методы Idb-keyval ориентированы на работу с ключами. Прямого метода поиска по значениям нет, поэтому необходимо комбинировать асинхронные операции для перебора данных и фильтрации по критериям.


Получение всех ключей и значений

Первый шаг при поиске по значениям — получить весь набор данных. Idb-keyval предоставляет методы keys() и get():

import { get, keys } from 'idb-keyval';

async function getAllEntries() {
    const allKeys = await keys();
    const entries = [];

    for (const key of allKeys) {
        const value = await get(key);
        entries.push({ key, value });
    }

    return entries;
}
  • keys() возвращает массив всех ключей в хранилище.
  • get(key) асинхронно получает значение для указанного ключа.
  • Цикл позволяет собрать массив объектов вида { key, value }, с которым удобно работать дальше.

Фильтрация данных по значению

Для поиска по конкретному критерию можно использовать стандартные методы массива, например filter():

async function searchByValue(searchTerm) {
    const entries = await getAllEntries();
    return entries.filter(entry => {
        // Предположим, что значения — строки или объекты с полем 'name'
        if (typeof entry.value === 'string') {
            return entry.value.includes(searchTerm);
        } else if (entry.value && typeof entry.value.name === 'string') {
            return entry.value.name.includes(searchTerm);
        }
        return false;
    });
}
  • В данном примере реализован поиск подстроки в строковом значении или в поле name объекта.
  • Фильтрация возвращает только те записи, которые соответствуют критерию.
  • Функция полностью асинхронная, что позволяет корректно работать с IndexedDB.

Оптимизация перебора больших наборов данных

При работе с большими хранилищами перебор всех ключей может быть дорогим по времени. Возможные оптимизации:

  1. Использование вспомогательного индекса Можно хранить отдельный объект с индексом значений, чтобы искать не по всему хранилищу, а по заранее сформированному списку ключей.
import { get, set } from 'idb-keyval';

async function addToIndex(key, value, indexKey = 'index') {
    const index = (await get(indexKey)) || {};
    index[key] = value.name || value;
    await set(indexKey, index);
}
  1. Поиск только по части данных Если известно, что значения имеют категориальный тип, можно хранить отдельные ключи для каждой категории и фильтровать только по ним.

  2. Пакетное получение значений Вместо последовательного await get(key) для каждого ключа можно использовать Promise.all:

async function getAllEntriesParallel() {
    const allKeys = await keys();
    const values = await Promise.all(allKeys.map(key => get(key)));
    return allKeys.map((key, i) => ({ key, value: values[i] }));
}

Это ускоряет процесс, особенно при большом количестве записей, благодаря параллельным обращениям к IndexedDB.


Поиск с поддержкой сложных критериев

Для сложных условий фильтрации удобно строить универсальные функции:

async function searchByPredicate(predicate) {
    const entries = await getAllEntriesParallel();
    return entries.filter(({ key, value }) => predicate(key, value));
}

// Пример использования:
const results = await searchByPredicate((key, value) => 
    value.age > 30 && value.name.startsWith('A')
);
  • predicate позволяет задавать любую логику поиска.
  • Подходит для фильтрации по нескольким полям или сложным условиям.
  • Поддерживает асинхронные операции через предварительное получение всех данных.

Интеграция поиска в пользовательский интерфейс

Часто поиск по значениям применяется для динамических списков или таблиц. Ключевые моменты:

  • Использовать дебаунсинг для ввода поисковых запросов, чтобы не выполнять поиск на каждое событие ввода.
  • Сохранять результаты в состоянии приложения, например в React или Vue, для повторного отображения без повторного обращения к IndexedDB.
  • При больших данных комбинировать поиск по индексам и фильтрацию на клиенте для ускорения отклика.
let timeout;
inputElement.addEventListener('input', (e) => {
    clearTimeout(timeout);
    const query = e.target.value;
    timeout = setTimeout(async () => {
        const results = await searchByValue(query);
        renderResults(results);
    }, 300);
});

Резюме ключевых техник

  • keys() и get() позволяют собрать весь набор данных для поиска.
  • filter() или пользовательский predicate обеспечивают гибкую фильтрацию по значению.
  • Параллельные вызовы через Promise.all ускоряют получение больших объемов данных.
  • Вспомогательные индексы и категории повышают эффективность поиска при масштабных хранилищах.
  • Интеграция с интерфейсом требует аккуратного управления событиями и состояния, чтобы сохранить производительность.