OceanBase源码解读:全文检索词项结构 ob_fts_struct

前面几篇我们把 OceanBase 4.3 全文检索(FTS)的分词器家族走了一遍——IK、N-gram、空白、英文基础分词器,最后到内置解析器的插件注册。文档被分词器切分之后,产出的一个个”词”在内存里用什么结构承载?答案就是本篇的主角 ObFTWord,它的实现落在 src/storage/fts/ob_fts_struct.cpp。这个文件全文只有 52 行,是 FTS 分支里最小的一个实现文件,却是整个倒排索引数据通路的地基:无论倒排字典、构建期的临时哈希表还是词项缓存,键的类型都是它。

核心数据结构:ObFTWord

ObFTWord 的成员在头文件中声明,实现文件里只贡献两个方法。它的设计非常克制:一个 word_ 指向词文本,一个 meta_(ObStringMeta)携带数据类型与 collation 信息。为什么要带 meta?因为同一个词在不同 collation 下语义不同——比如不区分大小写的 collation 下,”Hello” 和 “hello” 应当被视作同一个词。如果只做裸字节比较,倒排字典就会重复收录同义词项。meta 的存在让 ObFTWord 能”按 SQL 的语义”参与 hash 和比较。

关键流程一:hash()——词项的哈希计算

构建倒排索引或以词为 key 建哈希表时调用。它不是简单对字节串做 hash,而是先根据类型与 collation 从 datum 函数库取对应的默认 hash 函数:

int ObFTWord::hash(uint64_t &hash_val) const
{
  int ret = OB_SUCCESS;
  sql::ObExprBasicFuncs *funcs = ObDatumFuncs::get_basic_func(
      meta_.get_type(), meta_.get_collation_type());
  if (OB_ISNULL(funcs)) {
    ret = OB_ERR_UNEXPECTED;
  } else if (funcs->default_hash_ == nullptr) {
    ret = OB_ERR_UNEXPECTED;
  } else {
    ret = funcs->default_hash_(word_, 0, hash_val);
  }
  return ret;
}

这里的取舍很清晰:复用 SQL 表达式层的 ObDatumFuncs,保证词项哈希与 SQL 层同 collation 的哈希语义完全一致,避免为 FTS 单独维护一套哈希规则。取不到函数集或缺 default_hash_ 都按内部错误 OB_ERR_UNEXPECTED 处理——hash 失败可以安全地把错误向上传,因为调用方拿到错误码后会中止构建流程。

关键流程二:operator==()——词项相等比较

比较逻辑与 hash 同源:先按两侧 meta 生成 datum 比较函数,再执行比较。但它的失败处理策略截然不同:

bool ObFTWord::operator==(const ObFTWord &other) const
{
  bool is_equal = false;
  int ret = OB_SUCCESS;
  int cmp_ret = 0;
  ObDatumCmpFuncType func = get_datum_cmp_func(meta_, other.meta_);
  if (func == nullptr) {
    ob_abort();
  } else if (OB_FAIL(func(word_, other.word_, cmp_ret))) {
    ob_abort();
  } else {
    is_equal = (cmp_ret == 0);
  }
  return is_equal;
}

注意 operator== 无法向上返回错误码,而”比较失败仍然继续”的后果是倒排字典出现错误的去重结果——两个不同的词被当成同一个,或者同一个词被拆成两份。这类静默的数据正确性问题比崩溃严重得多,所以代码选择 ob_abort() 直接终止进程,属于典型的 fail-fast 设计。前提是调用方(构建器、扫描迭代器)已经保证 meta_ 正确初始化,正常路径永远不会走到 abort 分支。

在 FTS 全链路中的位置

把分词器系列和本篇串起来,FTS 写入链路就是:文档 → 分词器切词(ObIKFTParser / ObNgramFTParser / ObSpaceFTParser 等)→ 词项封装为 ObFTWord → 以 ObFTWord 为 key 聚合到倒排字典 → 生成倒排索引持久化。查询链路则反向:查询词经分词器处理后同样封装为 ObFTWord,用它做字典查找拿到 doc_id 列表。ObFTWord 正是这条链路上反复流转的”通用货币”。

小结

ob_fts_struct.cpp 虽然只有两个函数,但浓缩了两个值得记住的工程决策:一是哈希与比较全部委托给 datum 层按 collation 语义执行,保证 FTS 与 SQL 层行为一致;二是比较路径选择 fail-fast,用崩溃换数据正确性。至此,FTS 分支的源码文件(分词器家族 + 插件注册 + 停用词 + 词项结构)已全部解读完毕,后续将转向物化视图(mview)模块的其余文件。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注