news 2026/9/14 14:12:32

ScyllaDB 全文检索实战:fulltext_index 与 BM25 查询的完整解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ScyllaDB 全文检索实战:fulltext_index 与 BM25 查询的完整解析

ScyllaDB 全文检索实战:fulltext_index 与 BM25 查询的完整解析

【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb

本文围绕 ScyllaDB 的全文检索(Full-Text Search, FTS)功能展开:如何在text/varchar/ascii列上创建fulltext_index自定义索引、如何编写符合约束的BM25()查询,并结合当前仓库中的索引校验源码(index/fulltext_index.cc)与查询语句实现(cql3/statements/external_search/fulltext_indexed_table_select_statement.cc)说明每条限制背后的具体校验逻辑,以及配套的端到端测试(test/cqlpy/test_fulltext_search_with_mock.py)。读完后,你将能够正确建表、建索引、编写并排查 FTS 查询,并理解每个查询约束在源码中的拒绝点。

什么是全文检索

全文检索允许你在文本列中查找包含特定单词或短语的行。与精确匹配查询或LIKE过滤不同,FTS 使用倒排索引对文本分词,并基于 BM25 评分算法按相关性对结果排序。

典型使用场景包括:

  • 在产品描述、文章或日志消息中搜索关键词;
  • 按与查询词的匹配程度对结果进行相关性排序;
  • 过滤出至少包含一个匹配词的行。

创建 fulltext_index

在运行 FTS 查询之前,必须先对目标列创建fulltext_index

CREATE CUSTOM INDEX ON ks.t (v) USING 'fulltext_index';

可以通过WITH OPTIONS指定 analyzer,控制文本的分词方式,默认值为standard。各支持 analyzer 的完整说明见 CQL 二级索引文档:

CREATE CUSTOM INDEX ON ks.t (v) USING 'fulltext_index' WITH OPTIONS = {'analyzer': 'english'};

源码中的索引选项定义

从源码看,fulltext_index支持的选项在 index/fulltext_index.cc 中集中定义,共两个:

  • analyzer:指定用于分词的内置文本分析器。源码注释说明该列表对应后端搜索引擎(Tantivy)预期提供的分析器,合法取值为standardenglishgermanfrenchspanishitalianportugueserussiansimplewhitespace共 10 种(见 index/fulltext_index.cc);
  • positions:布尔值,控制是否在索引中存储 token 位置。短语查询(phrase queries)依赖位置信息;如需节省空间可设为false

传入任何未在此表中注册的选项,check_index_options会直接抛出Unsupported option ... for fulltext index异常(见 index/fulltext_index.cc)。

创建索引的硬性要求

官方文档明确要求如下,且每一条都能在索引校验逻辑fulltext_index::validate中得到印证(见 index/fulltext_index.cc,校验顺序为check_uses_tabletscheck_targetcheck_cdc_optionscheck_index_options):

  • 列类型:被索引列必须是textvarcharascii类型,其他类型会被拒绝。源码中check_target检查abstract_type::kind::utf8abstract_type::kind::ascii,不满足则抛出 "Fulltext index is only supported on text, varchar, or ascii columns..."(见 index/fulltext_index.cc)。此外,索引目标必须且只能有一个单列,分区键列不能作为目标。
  • 表必须使用 tablets(而非 vnodes)。对应validate中的check_uses_tablets(schema, db)
  • CDC 要求:表必须启用 CDC,且 TTL 至少为 86400 秒(24 小时),并满足delta = 'full'或启用 postimage。创建 fulltext 索引时 CDC 会被自动开启,无需手动配置。这个 86400 秒的门槛在基类中定义为常量VS_TTL_SECONDS = 86400,注释说明其目的是"确保 CDC 数据保留时间足够长,让索引构建能够完成"(见 index/external_index.hh)。
  • fulltext 索引继承自external_index基类(见 index/external_index.hh),该基类注释表明索引由外部存储引擎(Vector Store)支撑,这也是后文 CDC 依赖与查询走外部服务的原因。

Cell 级 TTL 的陷阱

使用标准USING TTL语法(在INSERTUPDATE上)设置的 cell 级 TTL,会在到期时间使该值不可读,但不会生成 CDC 事件,因此全文索引不会被更新,会为该值保留一条过期(stale)索引条目。若需要索引反映过期行为,应改用 Per-row TTL 特性:它会显式删除过期行,并会生成 CDC 事件,从而驱动索引更新。

使用 BM25 进行查询

FTS 查询使用BM25()函数对行进行搜索词打分。BM25()接收两个参数:列名和查询字符串。

一条合法的全文检索查询必须同时在以下两个子句中使用BM25(),且作用于同一列、使用同一搜索词

  • WHERE子句:形式必须严格为BM25(column, 'term') > 0,用于过滤出匹配搜索词的行;
  • ORDER BY子句:ORDER BY BM25(column, 'term'),按 BM25 相关性得分排序(得分最高者优先)。

两个子句缺一不可——仅有WHERE BM25()或仅有ORDER BY BM25()的查询都会被拒绝;并且两处必须引用同一列、使用同一搜索词。此外每条 FTS 查询都要求LIMIT。完整语法参考见 CQL SELECT 文档。

基础查询

过滤包含搜索词的行并按相关性排序:

SELECT * FROM ks.t WHERE BM25(v, 'search term') > 0 ORDER BY BM25(v, 'search term') LIMIT 10;

WHERE子句中,>是唯一支持的运算符,且右值必须是字面量0>==<<=!=等运算符以及任何非零阈值都会被拒绝。这一约束在源码validate_bm25_where_restriction中实现:运算符不是GT时抛出only ">" is supported,右值不是常量或反序列化后不等于0.0f时抛出comparison value must be the literal 0(见 cql3/statements/external_search/fulltext_indexed_table_select_statement.cc)。

与其他过滤条件组合

目前BM25()旁边不支持附加的WHERE限制(例如分区键等值比较),会被直接拒绝。从源码结构看,prepare阶段在确认恰好存在一个 BM25 评分限制后,还会检查分区键、聚簇列、非主键三类限制是否全部为空,只要有一项非空即抛出 "Full-text search queries do not support additional WHERE restrictions"(见 fulltext_indexed_table_select_statement.cc)。组合过滤能力已规划在未来版本中支持。

使用绑定标记

查询词可以在预处理语句中通过绑定标记传入:

SELECT * FROM ks.t WHERE BM25(v, ?) > 0 ORDER BY BM25(v, ?) LIMIT 10;

两个绑定标记在执行时都会被检查,必须绑定相同的值;WHEREORDER BY绑定不同值会导致查询被拒绝。源码中,两个搜索词若都是字面量则在 prepare 阶段即检查一致性;若涉及绑定标记,则在execute_search中通过expr::evaluate求值后逐一对比(见 fulltext_indexed_table_select_statement.cc)。测试用例test_bm25_two_bind_markers_search_termtest_bm25_named_bind_markers_search_term等验证了字面量与绑定标记混用时"搜索词必须一致"的行为,且命名绑定标记(:term)同样可用(见 test/cqlpy/test_fulltext_search_with_mock.py)。

与用户自定义函数重名时的区分

BM25不是保留字。如果某个 keyspace 中定义了名为bm25的用户自定义函数,未加限定的BM25()调用会产生歧义;此时应显式地用system.bm25(...)限定以选择内置算子。

FTS 查询约束汇总

FTS 查询强制执行以下规则:

约束说明
两个子句缺一不可查询必须同时包含WHERE BM25() > 0过滤和ORDER BY BM25()排序,且两者引用同一列、同一搜索词。单独任一子句都会被拒绝。
仅支持>与字面量0WHERE中唯一接受的形式是BM25(column, 'term') > 0,其他运算符(>==<<=!=)和非零阈值均被拒绝。
过滤组合受限WHERE中仅接受BM25(column, 'term') > 0,任何附加限制(如分区键等值)都会被拒绝;组合过滤已规划在未来版本支持。
LIMIT必填每条 FTS 查询必须包含不超过 1000 的LIMIT;缺少LIMITLIMIT大于 1000 都会被拒绝。
不支持PER PARTITION LIMITFTS 查询不能与PER PARTITION LIMIT一起使用。
不支持聚合FTS 查询不能包含聚合函数(如COUNT(*)SUM())。
必须有 fulltext 索引被查询列上必须存在fulltext_index,普通二级索引不满足该要求。
BM25()不能出现在SELECTBM25()仅在WHEREORDER BY子句有效,不能作为选择器。
仅支持单一排序ORDER BY BM25()不能与其他ORDER BY列、第二个BM25()排序或ANN排序组合。
不支持分页FTS 查询不支持 paging,最多LIMIT行的全部匹配结果在单页返回。
不支持分组FTS 查询不能包含GROUP BY子句。

源码纵深:一条 FTS 查询是如何被校验与执行的

fulltext_indexed_table_select_statement(见 cql3/statements/external_search/fulltext_indexed_table_select_statement.hh)是 FTS 查询的专用 SELECT 语句类型,其prepare阶段集中实现了上表大部分约束的拒绝逻辑:

  • LIMIT→ "Full-text search queries require a LIMIT";
  • 出现per_partition_limit→ 拒绝;
  • 选择器是聚合(selection->is_aggregate())→ 拒绝;
  • 缺少ORDER BY BM25()(无ordering_info)→ 拒绝;
  • WHERE中的 BM25 评分限制为空或超过 1 个 → 分别抛出 "require a WHERE BM25() > 0 clause" / "support only one WHERE BM25() restriction"。

在执行侧,execute_search(见 fulltext_indexed_table_select_statement.cc)还会做几件文档层面的约束对应的事:

  1. LIMIT 上限limit超过max_fts_query_limit = 1000(定义于 fulltext_indexed_table_select_statement.hh)时抛出异常;
  2. 搜索词非空:绑定标记求值结果为 null 时拒绝;
  3. 同词校验WHERE的搜索词与ORDER BY的搜索词求值后不一致时拒绝;
  4. 调用外部索引服务:通过query_processorvector_store_client().bm25(ks_name, index_name, schema, search_term, limit, ...)发起请求,取得带分数的主键列表后,再由external_score_provider将分数按行回填到结果中。

测试侧用 mock 服务验证了这条链路:FTS 查询会被翻译成发往 Vector Store 服务/bm25端点的 HTTP POST 请求,具体路径为/api/v1/indexes/<keyspace>/<index>/bm25,请求体携带查询词与 limit,返回的primary_keysscores决定了行序(见 test/cqlpy/test_fulltext_search_with_mock.py)。另一个索引侧的测试文件 test/cqlpy/test_fulltext_index.py 则覆盖建索引时的各类校验。

小结与延伸阅读

ScyllaDB 的全文检索由三部分构成:fulltext_index自定义索引(由外部 Vector Store/Tantivy 引擎支撑,依赖 tablets 与长 TTL 的 CDC 数据流)、严格的BM25()双条款查询语法(过滤 + 排序、同列同词、LIMIT ≤ 1000)、以及源码中逐条落地的 prepare/execute 两级校验。编写 FTS 功能时建议按以下顺序排查报错:先确认列类型与 tablets/CDC 前提(建索引报错时),再对照本文约束表核对 WHERE/ORDER BY/LIMIT 形态(查询报错时)。

更多细节可继续阅读仓库中的以下文件:

  • 官方功能文档:docs/features/fulltext-search.rst
  • 索引语法与 analyzer 说明:docs/cql/secondary-indexes.rst
  • BM25 查询语法参考:docs/cql/dml/select.rst
  • 内部设计说明:docs/dev/fulltext_search.md
  • 索引定义与校验:index/fulltext_index.hh、index/external_index.hh
  • 查询语句实现:cql3/statements/external_search/fulltext_indexed_table_select_statement.cc
  • 端到端测试:test/cqlpy/test_fulltext_search_with_mock.py、test/cqlpy/test_fulltext_index.py

【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:12:00

基于IEEE30节点系统的MATLAB潮流计算与电力系统仿真实践

简介&#xff1a;IEEE 30 节点测试系统的 MATLAB M 文件&#xff0c;面向电力系统专业学生与科研人员&#xff0c;可用于潮流计算、稳态分析及网络特性研究。文件以单个 .m 脚本形式封装了 30 节点系统的拓扑连接、节点注入功率、支路阻抗等关键参数&#xff0c;并给出可执行的…

作者头像 李华
网站建设 2026/9/14 14:11:57

基于YOLOv8的农田植保无人机喷洒盲区检测系统

简介&#xff1a;这是一套面向计算机视觉方向毕业设计、课程设计及初期项目演示的YOLOv8工程包&#xff0c;聚焦农田植保无人机喷洒覆盖盲区的检测场景&#xff0c;适合具备一定Python基础、希望快速跑通目标检测全流程的学生或开发者。压缩包共8个文件&#xff0c;主体包含3个…

作者头像 李华
网站建设 2026/9/14 14:11:44

高斯滤波与傅里叶变换的频域闭环解析

简介&#xff1a;本资源是一套面向图像处理初学者与MATLAB实践者的完整教学脚本包&#xff0c;聚焦高斯滤波降噪、频域分析&#xff08;傅里叶变换&#xff09;、数据归一化及多阶段可视化等核心技能&#xff0c;适用于课程实验、课程设计或自学进阶。压缩包共6个MATLAB源文件&…

作者头像 李华
网站建设 2026/9/14 14:11:42

基于Golang的长轮询推送方案:架构设计与生产实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:10:45

SSM健身房系统:毕业设计中的MyBatis与Spring事务实战

简介&#xff1a;这是一套面向计算机专业本科生的Java毕业设计实战项目&#xff0c;基于SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架开发的健身房管理系统&#xff0c;适用于毕设选题、课程设计及Java Web全栈能力训练。系统覆盖管理员、教练、会员、访客四类角色&…

作者头像 李华