Dexie.js предоставляет высокоуровневый интерфейс для работы с
IndexedDB, включая расширенные возможности индексированных выборок.
Одним из таких механизмов является inAnyRange(),
позволяющий выполнять запросы по нескольким диапазонам значений в рамках
одного индекса, избегая необходимости объединять результаты нескольких
отдельных запросов на стороне приложения.
Данный метод используется поверх индексированных полей и оптимизирует выборку, когда требуется получить данные, попадающие сразу в несколько диапазонов значений.
Базовая форма использования:
db.table.where(index).inAnyRange(ranges)
Где:
index — индексированное поле или составной индексranges — массив диапазоновКаждый диапазон задаётся как пара границ:
[
[lower1, upper1],
[lower2, upper2],
...
]
Также возможны дополнительные параметры границ:
Dexie.Range для более точного
контроляIndexedDB хранит данные в B-tree структуре по индексам.
inAnyRange() выполняет несколько диапазонных сканов по
одному индексу и объединяет результаты на уровне движка Dexie.
Логика работы:
Пример таблицы:
db.version(1).stores({
logs: "++id, timestamp, level"
});
Запрос по нескольким диапазонам времени:
db.logs
.where("timestamp")
.inAnyRange([
[1700000000000, 1700003600000],
[1700010000000, 1700013600000]
])
.toArray();
В этом случае выбираются записи из двух временных окон.
Более гибкий способ задания диапазонов:
import Dexie fr om "dexie";
db.logs
.wh ere("timestamp")
.inAnyRange([
Dexie.range(1700000000000, 1700003600000),
Dexie.range(1700010000000, 1700013600000).excludeUpperBound()
])
.toArray();
Возможности Dexie.Range:
below, above)При использовании compound index:
db.version(1).stores({
orders: "[userId+createdAt], status"
});
Запрос:
db.orders
.where("[userId+createdAt]")
.inAnyRange([
[ [1, 1700000000000], [1, 1700100000000] ],
[ [2, 1700000000000], [2, 1700100000000] ]
])
.toArray();
Каждый диапазон задаётся как пара составных ключей.
Важный момент: порядок полей в индексе строго влияет на корректность диапазона.
inAnyRange() и anyOf() решают похожие
задачи, но работают по-разному:
db.users.where("id").anyOf([1, 5, 9])
db.users.where("age").inAnyRange([
[18, 25],
[40, 60]
])
between() ограничен одним диапазоном:
db.users.where("age").between(18, 30)
inAnyRange() расширяет модель до множества
диапазонов:
db.users.where("age").inAnyRange([
[18, 30],
[50, 65]
])
При работе с inAnyRange() важно учитывать поведение
IndexedDB:
Каждый диапазон = отдельный индексный проход. Большое количество диапазонов может увеличивать время выполнения.
Рекомендуется:
Хотя Dexie нормализует входные данные, предварительная сортировка диапазонов может улучшить предсказуемость выполнения:
ranges.sort((a, b) => a[0] - b[0]);
Запросы быстрее при использовании узких индексов:
timestamp быстрее, чем JSON полеПри пересечении диапазонов возможны повторные совпадения. Dexie выполняет дедупликацию на уровне результата, но поведение зависит от структуры индекса.
Пример пересечения:
[
[10, 50],
[40, 80]
]
Значения 40–50 встречаются в обоих диапазонах, но итоговый результат возвращается без дублирования.
Как и большинство операций Dexie, inAnyRange()
возвращает Promise или Collection:
const result = await db.events
.where("timestamp")
.inAnyRange([
[t1, t2],
[t3, t4]
]);
Ленивая итерация возможна:
db.events
.where("timestamp")
.inAnyRange([...])
.each(event => {
// обработка по мере поступления
});
После inAnyRange() допустимо добавлять дополнительные
фильтры:
db.logs
.where("timestamp")
.inAnyRange([
[a, b],
[c, d]
])
.and(log => log.level === "error")
.toArray();
Важно: фильтр .and() выполняется в памяти, а не в
индексе.
Несмотря на гибкость, метод имеет ряд ограничений:
Разделение по периодам активности системы:
[
ночной_период,
дневной_период,
вечерний_период
]
db.records.where("id").inAnyRange([
[100, 199],
[500, 599]
]);
Например, возрастные группы:
[
[18, 25],
[30, 40],
[50, 65]
]
При больших наборах данных ключевыми факторами становятся:
В некоторых случаях inAnyRange() может быть заменён
на:
inAnyRange() полностью поддерживается внутри транзакций
Dexie:
db.transaction("r", db.logs, async () => {
return db.logs
.where("timestamp")
.inAnyRange([[a, b], [c, d]])
.toArray();
});
Это обеспечивает консистентность данных в рамках операции чтения.