Работа с IndexedDB в Dexie.js часто требует фильтрации записей не по одному значению, а сразу по набору допустимых или исключаемых значений. Для этого используются методы anyOf() и noneOf(), позволяющие строить эффективные индексированные запросы без перехода к полному перебору коллекции.
Эти операции выполняются на уровне индексов, что делает их
значительно быстрее, чем ручная фильтрация через
filter().
Метод anyOf() возвращает записи, у которых значение индексируемого поля совпадает хотя бы с одним элементом из переданного набора.
db.table.where('status').anyOf(['active', 'pending'])
В результате будут выбраны все записи, где поле status
имеет значение:
activependingconst db = new Dexie("app");
db.version(1).stores({
users: "++id, name, role"
});
await db.users.bulkAdd([
{ name: "Alex", role: "admin" },
{ name: "John", role: "editor" },
{ name: "Kate", role: "viewer" },
{ name: "Mike", role: "editor" }
]);
const result = await db.users
.where("role")
.anyOf(["admin", "editor"])
.toArray();
Результат содержит всех пользователей с ролями admin и
editor.
Метод одинаково работает как со строками, так и с числами.
db.orders.where("statusCode")
.anyOf([1, 3, 5])
.toArray();
Где:
1 — создан3 — в обработке5 — завершёнОсобенность:
anyOf([])
возвращает пустой результат, так как отсутствуют допустимые значения для сопоставления.
Ключевое условие эффективности anyOf() — наличие
индекса:
stores({
users: "++id, name, role"
})
Запрос:
db.users.where("role").anyOf(["admin", "editor"])
использует индекс role, избегая полного сканирования
таблицы.
Метод noneOf() выполняет обратную операцию: исключает записи, содержащие указанные значения.
db.table.where('status').noneOf(['deleted', 'blocked'])
const activeUsers = await db.users
.where("role")
.noneOf(["banned", "deleted"])
.toArray();
Результат содержит всех пользователей, кроме заблокированных и удалённых.
db.orders.where("priority")
.noneOf([0, 9])
.toArray();
Исключаются заказы с минимальным и максимальным приоритетом.
| Метод | Поведение |
|---|---|
| anyOf() | включает только совпадения |
| noneOf() | исключает указанные значения |
Методы могут комбинироваться с диапазонными запросами:
db.products
.where("price")
.above(100)
.anyOf([150, 200, 300])
Однако важно учитывать: anyOf() применяется к уже
ограниченной выборке.
При работе со строками учитывается точное совпадение:
db.tasks.where("tag")
.anyOf(["frontend", "backend"])
Частичные совпадения не учитываются:
"frontend-react" не совпадёт с
"frontend"Для таких случаев используется startsWith() или
filter().
Метод имеет важное ограничение:
При слишком длинных массивах исключений возможна деградация производительности.
При составных индексах:
db.version(1).stores({
logs: "++id, [level+source]"
});
Запрос:
db.logs
.where("[level+source]")
.anyOf([
["error", "api"],
["warn", "db"]
])
Каждый элемент массива соответствует полному ключу составного индекса.
Если индекс хранит сложные значения (например, массивы или
структуры), anyOf() и noneOf() работают только
с тем, что реально индексируется.
db.items.where("categoryIds")
.anyOf([1, 2, 3])
Если categoryIds — массив, требуется предварительное
нормализованное индексирование, иначе поведение будет ограничено.
При использовании:
where("field").anyOf([...])
Dexie.js:
Для noneOf() происходит аналогичный процесс, но с
фильтрацией исключений на уровне курсора.
db.users.filter(u => u.role === "admin" || u.role === "editor")
и
db.users.where("role").anyOf(["admin", "editor"])
разница принципиальная:
| Способ | Индекс используется | Производительность |
|---|---|---|
| anyOf() | да | высокая |
| filter() | нет | низкая (скан) |
anyOf([null, undefined])
работает только если такие значения реально индексируются и
присутствуют в данных. В IndexedDB undefined обычно не
хранится как индексируемое значение, что ограничивает использование.
При больших наборах значений:
anyOf([1,2,3,4,5,...1000])
Dexie формирует серию индексных диапазонов. При экстремальных размерах массива возможны:
В таких случаях используется разбиение запроса на батчи.
async function batchAnyOf(values, size = 100) {
const result = [];
for (let i = 0; i < values.length; i += size) {
const chunk = values.slice(i, i + size);
const part = await db.users
.where("role")
.anyOf(chunk)
.toArray();
result.push(...part);
}
return result;
}
При попытке:
db.table.where("nonIndexedField").anyOf([...])
Dexie может:
Оптимальная модель — всегда индексировать поля, используемые в anyOf/noneOf.
===)anyOf(["admin", "admin", "editor"])
эквивалентно:
anyOf(["admin", "editor"])
db.users
.where("role")
.anyOf(["admin", "editor"])
.filter(u => u.active)
.limit(10)
anyOf() выполняется на уровне индекса, после чего
остальные операции применяются к уже отфильтрованному набору.