indexeddb 不支持 sql 模糊匹配,高效方案是:1. 用索引+idbkeyrange 实现前缀搜索;2. 中间/结尾模糊需预处理 searchtext 字段+js 过滤;3. fuse.js 提升容错与体验;4. 统一小写存储、分层处理多条件。

IndexedDB 本身不支持 SQL 风格的 LIKE、通配符或正则模糊匹配,所谓“高效模糊搜索”必须在浏览器本地约束下做合理取舍:优先利用索引加速前缀匹配,对中间/结尾模糊等场景采用轻量 JS 过滤,并辅以预处理与缓存优化。纯靠游标全量遍历再 includes() 不可避免性能瓶颈,尤其数据量超千条时明显卡顿。
用索引 + IDBKeyRange 实现高效前缀搜索
这是唯一能真正借力 IndexedDB 原生能力的“类模糊”方案,适用于“以某字符串开头”的场景(如搜索 iphone → 匹配 iPhone 15、iPhone SE):
- 建索引时明确指定
{ unique: false },避免重复值被静默丢弃 - 查询时用
IDBKeyRange.bound(lower, upper, false, true),其中upper = lower + '\uffff'—— 这个 Unicode 最大字符确保字典序覆盖所有合法后缀 - 不要用
IDBKeyRange.only('abc%'),%在 IndexedDB 中无特殊含义,只是普通字符,不会触发通配 - 该方案天然支持空格、大小写混合字段(如 "Vue Router"),因字符串比较基于 UTF-16 字典序
对中间/结尾模糊采用预处理 + 游标过滤
当需支持 *test* 或 *ing 类匹配时,必须读取候选数据后由 JS 判断。为控制开销,关键在“缩小候选集”:
- 在存数据时预生成
searchText字段:合并标题与正文,转小写,可选去除标点(.replace(/[^\w\s]/g, '')) - 在
searchText上建立非唯一索引(如idx_search),用于后续游标遍历 - 查询时不直接全表扫描,而是先用前缀索引粗筛(如输入 “note”,先查所有
searchText以 “note” 开头的项),再对这批结果做.includes(query.toLowerCase()) - 限制返回数量(如最多 50 条),并用
AbortController支持中止长耗时遍历
用 Fuse.js 提升匹配质量与体验
若需容忍拼写错误、词序变化或按相关性排序,Fuse.js 是成熟可靠的客户端选择(压缩后仅 ~5KB):
- 它不替代 IndexedDB,而是作为“查询层”:从 IndexedDB 读出一批候选数据(例如按时间倒序取最近 1000 条),交由 Fuse 模糊匹配
- 配置
threshold: 0.4可放宽匹配容错,keys: ['title', 'content']支持多字段加权 - 搭配内存缓存(
Map存最近 10 个 query → result 映射)和输入防抖(300ms 停顿后再触发),体验接近原生搜索 - 注意:Fuse 不支持分页式流式加载,大数据量时应分批加载+增量匹配,避免单次构造过大数组
多条件与大小写处理的实用要点
真实搜索常需组合条件或忽略大小写,这些无法靠索引自动完成,但可通过结构设计简化逻辑:
- 大小写敏感是默认行为,统一在存入
searchText时转小写,查询时也统一小写,避免每次遍历都调用toLowerCase() - 多条件(如“含 ‘react’ 且创建于 2025 年后”)建议分层处理:先用索引快速筛选时间范围(如有
createdAt索引),再对结果集用 JS 做字符串匹配和布尔判断 - 避免嵌套游标——即不用一个游标查出 A 条,再对每条发起另一次游标查 B 字段;应把所需字段都存在同一对象中,一次读取到位
- 高频低变更数据(如标签列表、分类树)可额外建只读缓存对象仓库,减少重复计算











