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_indexCREATE 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预期提供的分析器合法取值为standard、english、german、french、spanish、italian、portuguese、russian、simple、whitespace共 10 种见 index/fulltext_index.ccpositions布尔值控制是否在索引中存储 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_tablets→check_target→check_cdc_options→check_index_options列类型被索引列必须是text、varchar或ascii类型其他类型会被拒绝。源码中check_target检查abstract_type::kind::utf8与abstract_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语法在INSERT或UPDATE上设置的 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;两个绑定标记在执行时都会被检查必须绑定相同的值WHERE与ORDER BY绑定不同值会导致查询被拒绝。源码中两个搜索词若都是字面量则在 prepare 阶段即检查一致性若涉及绑定标记则在execute_search中通过expr::evaluate求值后逐一对比见 fulltext_indexed_table_select_statement.cc。测试用例test_bm25_two_bind_markers_search_term、test_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缺少LIMIT或LIMIT大于 1000 都会被拒绝。不支持PER PARTITION LIMITFTS 查询不能与PER PARTITION LIMIT一起使用。不支持聚合FTS 查询不能包含聚合函数如COUNT(*)、SUM()。必须有 fulltext 索引被查询列上必须存在fulltext_index普通二级索引不满足该要求。BM25()不能出现在SELECTBM25()仅在WHERE与ORDER 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还会做几件文档层面的约束对应的事LIMIT 上限limit超过max_fts_query_limit 1000定义于 fulltext_indexed_table_select_statement.hh时抛出异常搜索词非空绑定标记求值结果为 null 时拒绝同词校验WHERE的搜索词与ORDER BY的搜索词求值后不一致时拒绝调用外部索引服务通过query_processor的vector_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_keys与scores决定了行序见 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.rstBM25 查询语法参考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),仅供参考