一个开发团队将 OpenSearch 与 SQLite FTS5 结合使用,将视频搜索结果为空的比例从 11.4% 降低到了 2.1%,同时将延迟保持在 20 ms 以下。现在,用户输入 “blackpink jenny solo stag” 时,会看到正确的 “BLACKPINK Jennie SOLO stage”,而不是一个空列表。
为什么需要这一改进
一家视频托管平台的搜索日志显示了一个反复出现的问题:拉丁字母标题中的一个拼写错误就可能导致所有匹配项消失。SQLite 的 FTS5 扩展因其在处理中日韩(CJK)文本子字符串匹配方面的能力而备受推崇,但它并不支持模糊匹配。姓名或歌曲标题中一个拼写错误的字符就会导致整个查询失效。
现有的流水线将 SQLite 作为唯一的索引。它能很好地处理 CJK 查询,但对于拉丁字母的拼写错误没有任何容错机制。因此,团队开始寻找一种互补的搜索引擎,既能提供拼写容错能力,又不会舍弃已验证的 FTS5 层。
如何引入 OpenSearch
OpenSearch 作为一线搜索服务运行;SQLite 仍作为事实来源(source of truth)。这两个系统并行运行:首先由 OpenSearch 接收用户查询,如果响应足够快,则显示其结果。如果 OpenSearch 超时或报错,请求将回退(fallback)到 SQLite FTS5 索引。这种“故障安全”(fail-safe)设计确保了网络波动绝不会导致搜索栏显示为空。
多字段映射
每个视频标题在 OpenSearch 中通过三种方式进行索引:
- title.std – 由带有 ASCII folding(ASCII 折叠)的标准分析器处理。这可以规范化带重音的字符,并处理大多数拉丁字母拼写错误。
- title.cjk – 由 CJK 分析器处理,生成 bigrams(二元语法/双字符标记)。这保留了 FTS5 为亚洲文字提供的子字符串匹配优势。
- title.keyword – 以原样存储,用于精确匹配查找和排序。
独立的字段允许查询对每种脚本应用正确的分析,而不会混淆分词策略。
权重分层 (Boost tiers)
团队没有使用单一的庞大查询,而是构建了一个自动对结果进行排序的分层查询:
title.keyword上的精确短语匹配获得最高权重,确保完美匹配的结果占据列表首位。title.cjk上的 CJK bigram 匹配获得中等权重,以保持亚洲语言搜索的质量。title.std上的模糊拉丁匹配获得较低权重,允许拼写容错的结果出现,但不会掩盖精确匹配的结果。
这种分层方法使调优变得非常简单:调整一个权重值即可改变整类匹配结果的相对重要性。
智能模糊匹配
模糊匹配(Fuzziness)——允许有限次数的字符编辑——仅应用于拉丁字母字段。团队禁用了 title.cjk 的模糊匹配,因为在 CJK 文本中,单个字符的变化往往会完全改变含义。对于拉丁文本,查询使用了 OpenSearch 的 AUTO 模糊设置,该设置根据单词长度按比例调整允许的编辑距离,从而在容错性和相关性之间取得平衡。
性能与回退逻辑
搜索程序将 OpenSearch 调用封装在 try-catch 块中:
- 如果 OpenSearch 在 400 ms 内返回,则显示其结果。
- 如果调用抛出异常或超过超时时间,系统会立即针对 SQLite FTS5 重新运行查询。
这确保了网络延迟或服务中断绝不会降低用户体验。搜索延迟保持在 20 ms 以下。
可衡量的影响
- 拉丁字母查询的零结果率从 11.4% 降至 2.1%。
- CJK 查询的搜索质量保持不变,证实了新的 CJK 分析器保留了原始 FTS5 索引的优势。
- 端到端延迟始终稳定在 20 ms 目标以下,这意味着新增的层并没有拖慢 UI 的速度。
教训与权衡
- 将折叠(Folding)与模糊匹配(Fuzziness)分离 – 折叠(字符规范化)和模糊匹配(处理拼写错误)解决的是不同的问题。将它们放在不同的字段中可以避免产生意外的相互作用。
- 不要将搜索索引视为事实来源 – SQLite 仍然是规范存储(canonical store);OpenSearch 是一个派生的、可刷新的视图。这可以防止索引漂移,并简化故障后的恢复工作。
- 权重分层简化了调优 – 将相关的匹配项归入单一权重因子下,减少了需要调整的参数数量。
后续关注点
实验证明,引入轻量级的 OpenSearch 层可以显著提升多语言视频标题的拼写容错能力,同时不会牺牲 SQLite FTS5 成熟的 CJK 处理能力。对于搜索相关性直接影响观看时长的平台而言,这种改进能够转化为切实的用户体验提升。
