Searcher 类作为上层封装,依赖底层索引文件实现具体业务逻辑。核心职责是处理用户搜索词,并根据匹配结果返回网页信息。
1. 单例模式
初始化阶段采用单例模式管理索引对象,确保全局唯一性并构建正倒排索引。
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 工具将用户提供的关键字分词并交给 words 容器。
2.2 触发
从单例索引中获取倒排列表,通过 index->GetInvertedList(w) 获取关键词对应的倒排索引列表,存入哈希表 tokens_map。由于哈希结构特性,可自动实现去重。调用 boost::to_lower 统一转为小写,避免大小写区分导致的关键字失效。
遍历倒排列表时,元素引用直接操作哈希表中的节点,避免重复查找提高效率。
2.3 合并
将处理好的倒排数据统一存入 inverted_list_all,利用 vector 访问更方便的特性,接着根据权重进行从大到小排序。
注:代码中频繁提及正排与倒排索引,但核心都关联到
inverted_list_all。它是对多个倒排拉链进行'去重、合并、排序'后的候选文档集合,是连接'索引查询'和'结果返回'的中间数据结构。
2.4 构建 JSON
最后进行序列化,将内存中的 JSON 对象转换为标准传输格式。此处使用 JsonCpp 库的非标准库实现,无需手动编写序列化逻辑。将单个序列化的结果追加到根节点 root,最终通过 StyledWriter 写入可读性强的 JSON 字符串。
// query 为搜索关键字,json_string 是返回给用户的搜索结果
void Search(const std::string& query, std::string* json_string) {
// 1. 分词
std::vector<std::string> words;
ns_util::JiebaUtil::(query, &words);
std::vector<InvertedElemPrint> inverted_list_all;
std::unordered_map<, InvertedElemPrint> tokens_map;
(std::string w : words) {
boost::(w);
ns_index::InvertedList* inverted_list = index->(w);
(inverted_list == ) ;
( & elem : *inverted_list) {
& item = tokens_map[elem.doc_id];
item.doc_id = elem.doc_id;
item.weight += elem.weight;
item.words.(elem.word);
}
}
( & item : tokens_map)
inverted_list_all.(std::(item.second));
std::(inverted_list_all.(), inverted_list_all.(),
[]( InvertedElemPrint& e1, InvertedElemPrint& e2) {
eweight > eweight;
});
Json::Value root;
(& item : inverted_list_all) {
ns_index::DocInfo* doc = index->(item.doc_id);
(doc == ) ;
Json::Value elem;
elem[] = doc->title;
elem[] = (doc->content, item.words[]);
elem[] = doc->url;
root.(elem);
}
Json::StyledWriter writer;
*json_string = writer.(root);
}


