Метод filter() в Dexie.js применяется для постобработки
выборки, полученной из IndexedDB, и позволяет накладывать произвольные
условия на уже извлечённые данные. В отличие от индексных операций
(where()), filter() работает на уровне
JavaScript и выполняет проверку каждого объекта коллекции в памяти.
Ключевая особенность:
filter() не использует индексы
IndexedDB, поэтому всегда следует после первичного извлечения
данных.
filter() в
цепочке запросовDexie.js строит запросы как цепочку операций над объектом
Collection. Типичный порядок выглядит так:
where(),
toCollection())between,
above, below)filter())map(), each())toArray(), first(),
count())Пример базовой цепочки:
db.users
.where('age')
.above(18)
.filter(user => user.isActive && user.country === 'KZ')
.toArray();
filter()Метод принимает синхронную функцию-предикат:
collection.filter((item, index, array) => boolean)
Параметры:
item — текущий объект записиindex — позиция в результирующей коллекцииarray — массив (виртуальный, не всегда материализован
полностью)Возвращаемое значение:
true — элемент сохраняется в результатеfalse — элемент исключаетсяwhere()filter() часто используется неправильно как замена
индексным запросам, что приводит к деградации производительности.
where():filter():Пример неправильного подхода:
db.orders
.toCollection()
.filter(order => order.total > 1000);
Этот код загружает ВСЕ записи таблицы перед фильтрацией.
filter() применяется только после максимального сужения
выборки через индексы:
db.orders
.where('status')
.equals('paid')
.filter(order => order.total > 1000 && order.items.length > 3)
.toArray();
Здесь:
where('status') уменьшает объём данныхfilter() выполняет сложную бизнес-логикуКаждый вызов filter():
Сложность:
O(n) по количеству элементов после индексации
Важно учитывать:
until, limit)filter() часто используется в цепочках с
преобразованиями данных.
map():db.products
.where('inStock')
.equals(1)
.filter(p => p.price < 5000)
.map(p => ({
id: p.id,
label: p.name
}))
.toArray();
sortBy():db.users
.toCollection()
.filter(u => u.age >= 21)
.sortBy('lastLogin');
Важно учитывать: сортировка после filter() происходит
уже в памяти.
При обработке больших таблиц использование filter()
может привести к:
Типичный анти-паттерн:
db.logs
.toCollection()
.filter(log => log.message.includes('error'))
.toArray();
Если таблица содержит миллионы записей, весь набор будет загружен в память.
filter() принимает только синхронную функцию. Это
принципиальное ограничение Dexie.js.
Нельзя:
.filter(async item => await check(item)) // не поддерживается логически
Причина:
until()Для раннего завершения обработки иногда используется
until(), но он работает иначе:
db.items
.orderBy('created')
.filter(i => i.active)
.until(i => i.archived === true);
Здесь until() может сократить количество обрабатываемых
элементов, но не заменяет filter().
db.payments
.where('status')
.equals('completed')
.filter(p =>
p.amount > 100 &&
p.currency === 'USD' &&
p.refunded === false
)
.toArray();
db.sessions
.toCollection()
.filter(s =>
s.device === 'mobile' &&
s.duration > 300 &&
s.country === 'KZ'
);
Когда нет составного индекса, filter() становится
единственным вариантом.
filter() и составные
индексыВо многих случаях filter() используется там, где
правильнее создать compound index.
Плохой вариант:
db.users
.toCollection()
.filter(u => u.country === 'KZ' && u.age > 30);
Лучше:
db.users.where('[country+age]').between(['KZ', 30], ['KZ', 99]);
Вывод: filter() компенсирует отсутствие индексов, но не
заменяет их.
reverse() и offset()Порядок операций влияет на результат:
db.orders
.where('status')
.equals('paid')
.offset(10)
.filter(o => o.total > 100)
.reverse();
Здесь:
offset() применяется до filter()reverse() применяется послеЭто важно, потому что filter() не изменяет порядок
индексации, а лишь отсекает элементы.
Внутренне filter():
CollectionЭто означает:
Основные проблемы использования:
filter()Антипаттерн:
let threshold = 100;
db.items
.toCollection()
.filter(i => i.value > threshold++);
Такой код нарушает детерминированность запроса.
Использование filter() оправдано, когда:
Не оправдано: