IndexedDB复合索引需按字段顺序连续前缀匹配,必须用openCursor配合IDBKeyRange实现高效联合查询,非索引字段须在游标中手动过滤。

IndexedDB 的复合索引能高效支持多条件联合查询,关键在于索引字段顺序、查询范围匹配和游标遍历逻辑。直接用 get() 或 getAll() 无法发挥复合索引优势,必须配合 openCursor() 或 openKeyCursor() 使用游标,并正确构造 IDBKeyRange。
定义复合索引时注意字段顺序
复合索引的字段顺序直接影响可查询性。IndexedDB 要求查询条件必须从左到右“连续前缀匹配”——即如果索引是 ['status', 'category', 'createdAt'],那么以下查询可用该索引:
-
status === 'active'(只用第一个字段) -
status === 'active' && category === 'news'(前两个字段) -
status === 'active' && category === 'news' && createdAt >= Date.now() - 86400000(全部三个字段,且最后一个可范围查询)
但 category === 'news' && createdAt >= ... 就无法使用该索引(跳过了 status),会退化为全表扫描。
用 IDBKeyRange 构建多字段范围条件
对复合索引查询,需将多个条件组合成一个结构化键值范围。例如索引 ['status', 'score'],要查 status === 'published' 且 score >= 80:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
// ✅ 正确:左边界固定 status,score 设下限
const lowerBound = ['published', 80];
const upperBound = ['published', Infinity]; // 或用 ['published\uFFFF'] 防止 Unicode 问题
const range = IDBKeyRange.bound(lowerBound, upperBound);
<p>objectStore.index('status_score').openCursor(range);
若还需 score <= 95,则改用 IDBKeyRange.bound(['published', 80], ['published', 95])。注意:字符串字段需确保字典序安全,数字字段直接比较即可。
游标遍历中过滤非索引字段
复合索引只能加速索引字段的匹配,其余字段(如 authorId)必须在游标循环中手动过滤:
index.openCursor(range).onsuccess = function (e) {
const cursor = e.target.result;
if (cursor) {
const item = cursor.value;
// ✅ 索引字段已由 range 保证,额外条件在此判断
if (item.authorId === 123 && item.tags.includes('featured')) {
results.push(item);
}
cursor.continue();
}
};
避免在游标外做全量 getAll() 后再 filter,否则失去索引意义。也尽量减少每次 cursor 中的复杂计算。
处理等值 + 范围混合查询的技巧
当查询含多个等值条件 + 一个范围条件(如 type === 'post' && state === 'draft' && updatedAt > 1710000000000),索引应按 ['type', 'state', 'updatedAt'] 定义。此时:
- 前两个字段用精确匹配(
IDBKeyRange.only(['post', 'draft'])不适用,因第三个字段需范围) - 改用
IDBKeyRange.lowerBound(['post', 'draft', 1710000000000]) - 若还限制
updatedAt < 1710100000000,就用IDBKeyRange.bound(..., [..., 1710100000000])
记住:只要范围条件出现在复合索引最右侧字段,就能高效利用索引;中间字段一旦出现范围(如 ['a', 'b', 'c'] 中对 b 做 >),后续字段索引失效。

















