这个 Searcher 模块本质上是对底层索引功能的上层封装,主要负责处理用户搜索词并返回结果。其核心流程包括初始化、分词、触发匹配、合并排序以及构建 JSON 响应。
1. 单例模式与索引初始化
为了高效管理资源,我们采用单例模式来实例化索引对象。在初始化阶段,获取全局唯一的 Index 实例并根据输入数据建立索引。
private:
ns_index::Index* index;
public:
Searcher() {}
~Searcher() {}
public:
void InitSearcher(const std::string& input) {
// 1. 创建(获取)一个 index 对象,使用单例模式
index = ns_index::Index::GetInstance();
// 2. 根据对象建立索引
index->BuildIndex(input);
LOG1(NORMAL, "建立索引成功...");
}
};
2. 搜索核心流程
Search 函数是查询的核心,主要包含四个步骤:分词、触发、合并排序和构建 JSON。
2.1 分词
首先将用户输入的查询字符串进行切分。这里使用了 Jieba 工具类将关键字转换为单词数组。
std::vector<std::string> words;
ns_util::JiebaUtil::CutString(query, &words);
2.2 触发与去重
获取倒排索引列表后,我们需要将其映射到哈希表中。这一步非常关键,因为同一个文档可能匹配多个关键词,直接拼接会导致重复。利用 unordered_map 的特性,以 doc_id 为键自动实现去重,同时累加权重。
注意大小写不敏感处理,统一转为小写后再查询。
std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;
for (std::string w : words) {
boost::to_lower(w); // 忽略大小写
ns_index::InvertedList* inverted_list = index->GetInvertedList(w);
if (inverted_list == nullptr) continue;
for (const auto &elem : *inverted_list) {
&item = tokens_map[elem.doc_id];
item.doc_id = elem.doc_id;
item.weight += elem.weight;
item.words.(elem.word);
}
}


