C++之基于正倒排索引的Boost搜索引擎项目searcher部分代码及详解

C++之基于正倒排索引的Boost搜索引擎项目searcher部分代码及详解
这个searcher.hpp的本质是一种使用其他文件,然后实现自己功能的一种更上层的封装。

它主要实现的是就是他用户的搜索词进行处理,接着根据这个处理结果来返回网页给用户。

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); //std::cout<<"建立索引成功"<<std::endl; LOG1(NORMAL,"建立索引成功..."); }

这个函数的主要流程包括分词触发合并排序构建 JSON 结果四个步骤。

2.1 分词

分词的话我们就是先创建一个words数组,然后使用CutSreing函数把用户提供的关键字分词并交给words。

2.2 触发

这一部分的话获取在单例模式中的倒排索引,通过index->GetInvertedList(w)获取关键词w对应的倒排索引列表,然后把他交给tokens_map,又因为tokens_map是哈希结构,所以会自动实现去重。那个to_lower是用来实现小写化的,因为我们不可以说把用户输入的大写和小写区分成两个关键字。

for(const auto &elem : *inverted_list)里面的item是加了&的,所以实际上加的是tokens_map。

2.3 合并

这边就是把处理好的倒排拉链全部交给inverted_list_all,这边之所以要交给它是因为vector访问更方便,接着把他们根据自身的权重来进行从大到小排序。

2.4 构建 JSON

这一步就是序列化,简单来说就是把我们的内容转变成标准的、线性的、可存储 / 可传输的格式。当然最后也需要通过反序列化来读取正确的内容。这边序列化的代码我们不自己写了,而是通过Json的非标准库来实现。把单个序列化的结果交给总的root。

Json::StyledWriter writer; 这行代码的作用是创建一个 “格式化的 JSON 写入器”,用于将内存中的 JSON 对象(如代码中的root)转换为带缩进、换行的可读性强的 JSON 字符串,最后通过write交给json_string这个字符串。

PS:在这份代码里面一会是获取正排索引,一会是获取倒排索引,但是各位有没有发现,他们都和inverted_list_all有关系,inverted_list_all的本质是对多个倒排拉链进行 “去重、合并、排序” 后的候选文档集合,是连接 “索引查询” 和 “结果返回” 的中间数据结构
//queue是要搜索的关键字 //json_string是返回给用户的搜索结果 void Search(const std::string& query,std::string* json_string) { //1. 分词 std::vector<std::string>words; ns_util::JiebaUsutl::CutString(query,&words);//所以jieba里面的函数来进行分词 //2. 触发 //ns_index::InvertedList inverted_list_all; std::vector<InvertedElemPrint> inverted_list_all;//用来存放去重后的关键字 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; //inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end()); for(const auto &elem : *inverted_list){ //这个for循环就相当于把这个inverted_list里面的值交给这个哈希 auto &item = tokens_map[elem.doc_id]; //[]:如果存在直接获取,如果不存在新建 //注意:这个item是&,所以实际上加的是tokens_map的[elem.doc_id],这样写是为了避免重复查找,提高效率 //item一定是doc_id相同的print节点 item.doc_id = elem.doc_id; item.weight += elem.weight; item.words.push_back(elem.word); } } //3. 合并 for(const auto &item : tokens_map)//这边就是把通过哈希去重分好后的词交给inverted_list_all inverted_list_all.push_back(std::move(item.second)); /* std::sort(inverted_list_all.begin(),inverted_list_all.end(), [](const ns_index::InvertedElem& e1,const ns_index::InvertedElem& e2){//根据weight来从大到小排序 return e1.weight>e2.weight; });*/ std::sort(inverted_list_all.begin(), inverted_list_all.end(), [](const InvertedElemPrint &e1, const InvertedElemPrint &e2){ return e1.weight > e2.weight; }); //4. 构建 Json::Value root; for(auto& item:inverted_list_all) { ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id); if(doc==nullptr) continue; Json::Value elem;//通过json来进行序列化 elem["title"]=doc->title; elem["desc"]=GetDesc(doc->content,item.words[0]); elem["url"]=doc->url;//这是通过json然后可以将root序列化为JSON格式的字符串接着搜索结果以JSON形式返回。 root.append(elem); } // Json::StyledWriter writer; *json_string=writer.write(root);//完成序列化 }

3. GetDesc

这段代码就是寻找摘要,简单来说就是在html_content里面查找是否出现了关键字word。就相当于我们搜索一个词时,浏览器帮我们选出和这个词相关的网页信息的过程。

下面那几个if条件判断就是在确认这条网页信息的程度够不够,如果不够的话就直接全部拿过来,因为如果说没有怎么长我们还前50后100,那就会越界访问。

 //这个函数的作用就是找摘要,比如说关键词K,那么我们就把K前面和后面的一部分内容作为摘要 std::string GetDesc(const std::string& html_content,const std::string& word) { int prev_step=50; int next_step=100; auto iter=std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),[](int x,int y){ return (std::tolower(x)==std::tolower(y)); });//在 html_content 这个字符串中查找 word 这个子序列 //return (std::tolower(x)==std::tolower(y))用来忽略大小写 if(iter==html_content.end()) return "None1"; int pos=std::distance(html_content.begin(),iter);//计算找到的子序列在 html_content 中的起始位置 pos。 if(pos==std::string::npos) return "None1"; int start=0; int end=html_content.size()-1; if(pos-prev_step>start) start=pos-prev_step; if(pos+next_step<end) end=pos+next_step; if(start>=end) return "None2"; std::string desc=html_content.substr(start,end-start); desc+="..."; return desc; }

4. InvertedElemPrint

这个结构体的出现就是为了解决重复文档的问题。作为 “去重与信息聚合” 的载体,解决 “同一文档因匹配多个关键词而重复出现” 的问题。

通过 doc_id(文档唯一标识)作为核心依据,配合哈希表(tokens_map)实现 “同一文档只被记录一次”。当用户查询包含多个关键词时,同一文档可能匹配多个关键词并出现在多个倒排拉链中,InvertedElemPrint 借助哈希表的键唯一性,避免文档在结果中重复出现。

struct InvertedElemPrint{//这个结构体的作用就是用来解决重复文档的问题 uint64_t doc_id; int weight; std::vector<std::string> words; InvertedElemPrint(): doc_id(0), weight(0) {} };

5. 总结

通过 “索引构建” 提前做好数据准备,通过 “查询处理” 高效匹配并排序文档,通过 “结果格式化” 衔接前端展示,最终实现 “用户输入关键词→得到清晰搜索结果” 的完整功能,以下是它的完整代码:

#pragma once #include"index.hpp" #include"usuallytool.hpp" #include<algorithm> #include<jsoncpp/json/json.h> #include"log.hpp" namespace ns_searcher{ struct InvertedElemPrint{//这个结构体的作用就是用来解决重复文档的问题 uint64_t doc_id; int weight; std::vector<std::string> words; InvertedElemPrint(): doc_id(0), weight(0) {} }; class Searcher{ 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); //std::cout<<"建立索引成功"<<std::endl; LOG1(NORMAL,"建立索引成功..."); } //queue是要搜索的关键字 //json_string是返回给用户的搜索结果 void Search(const std::string& query,std::string* json_string) { //1. 分词 std::vector<std::string>words; ns_util::JiebaUsutl::CutString(query,&words);//所以jieba里面的函数来进行分词 //2. 触发 //ns_index::InvertedList inverted_list_all; std::vector<InvertedElemPrint> inverted_list_all;//用来存放去重后的关键字 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; //inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end()); for(const auto &elem : *inverted_list){ //这个for循环就相当于把这个inverted_list里面的值交给这个哈希 auto &item = tokens_map[elem.doc_id]; //[]:如果存在直接获取,如果不存在新建 //注意:这个item是&,所以实际上加的是tokens_map的[elem.doc_id],这样写是为了避免重复查找,提高效率 //item一定是doc_id相同的print节点 item.doc_id = elem.doc_id; item.weight += elem.weight; item.words.push_back(elem.word); } } //3. 合并 for(const auto &item : tokens_map)//这边就是把通过哈希去重分好后的词交给inverted_list_all inverted_list_all.push_back(std::move(item.second)); /* std::sort(inverted_list_all.begin(),inverted_list_all.end(), [](const ns_index::InvertedElem& e1,const ns_index::InvertedElem& e2){//根据weight来从大到小排序 return e1.weight>e2.weight; });*/ std::sort(inverted_list_all.begin(), inverted_list_all.end(), [](const InvertedElemPrint &e1, const InvertedElemPrint &e2){ return e1.weight > e2.weight; }); //4. 构建 Json::Value root; for(auto& item:inverted_list_all) { ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id); if(doc==nullptr) continue; Json::Value elem;//通过json来进行序列化 elem["title"]=doc->title; elem["desc"]=GetDesc(doc->content,item.words[0]); elem["url"]=doc->url;//这是通过json然后可以将root序列化为JSON格式的字符串接着搜索结果以JSON形式返回。 root.append(elem); } // Json::StyledWriter writer; *json_string=writer.write(root);//完成序列化 } //这个函数的作用就是找摘要,比如说关键词K,那么我们就把K前面和后面的一部分内容作为摘要 std::string GetDesc(const std::string& html_content,const std::string& word) { int prev_step=50; int next_step=100; auto iter=std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),[](int x,int y){ return (std::tolower(x)==std::tolower(y)); });//在 html_content 这个字符串中查找 word 这个子序列 //return (std::tolower(x)==std::tolower(y))用来忽略大小写 if(iter==html_content.end()) return "None1"; int pos=std::distance(html_content.begin(),iter);//计算找到的子序列在 html_content 中的起始位置 pos。 if(pos==std::string::npos) return "None1"; int start=0; int end=html_content.size()-1; if(pos-prev_step>start) start=pos-prev_step; if(pos+next_step<end) end=pos+next_step; if(start>=end) return "None2"; std::string desc=html_content.substr(start,end-start); desc+="..."; return desc; } }; } 

Read more

不止“996”!曝硅谷AI创业圈「极限工作制」:每天16小时、凌晨3点下班、周末也在写代码

不止“996”!曝硅谷AI创业圈「极限工作制」:每天16小时、凌晨3点下班、周末也在写代码

编译 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) “如果你周日去旧金山的咖啡馆,会发现几乎每个人都在工作。” 这是 AI 创业公司 Mythril 联合创始人 Sanju Lokuhitige 最近最直观的感受。去年 11 月,他特地搬到旧金山,只为了更接近 AI 创业浪潮的中心。但很快,他也被卷入了这股浪潮带来的另一面——一种越来越极端的工作文化。 Lokuhitige 坦言,他现在几乎每天工作 12 小时,每周 7 天。除了每周少数几场刻意安排的社交活动(主要是为了和创业者们建立联系),其余时间几乎都在写代码、做产品。 “有时候我整整一天都在编程,”他说,“我基本没有什么工作与生活的平衡。”而这样的生活,在如今的 AI 创业圈里并不算罕见。 旧金山 AI 创业圈的真实日常 一位在旧金山一家 AI

By Ne0inhk
黄仁勋公开发文:传统软件开发模式终结,参与AI不必非得拥有计算机博士学位

黄仁勋公开发文:传统软件开发模式终结,参与AI不必非得拥有计算机博士学位

AI 究竟是什么?在 NVIDIA CEO 黄仁勋看来,它早已不只是聊天机器人或某个大模型,而是一种正在迅速成形的“新型基础设施”。 近日,黄仁勋在英伟达官网发布了一篇长文,提出一个颇具形象的比喻——AI 就像一块“五层蛋糕”。从最底层的能源,到芯片、基础设施、模型,再到最上层的应用,人工智能正在形成一整套完整的产业技术栈,并像电力和互联网一样,逐渐成为现代社会的底层能力。 这也是黄仁勋自 2016 年以来公开发表的第七篇长文。在这篇文章中,他从计算机发展史与第一性原理出发,试图解释 AI 技术栈为何会演化成如今的形态,以及为什么全球正在掀起一场规模空前的 AI 基础设施建设。 在他看来,过去几十年的软件大多是预先编写好的程序:人类设计好算法,计算机按指令执行,数据被结构化存储在数据库中,通过精确查询调用。而 AI 的出现打破了这一模式——计算机开始能够理解图像、文本和声音,并根据上下文实时生成答案、推理结果甚至新的内容。 正因为智能不再是预先写好的代码,而是实时生成的能力,支撑它运行的整个计算体系也必须被重新设计。

By Ne0inhk
猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 过去几年里,科技公司几乎都在同一件事上加速:让 AI 参与写代码。 从自动补全、自动生成函数,到直接修改系统配置,生成式 AI 已经逐渐走进真实生产环境。但最近发生在亚马逊的一连串事故,却给整个行业泼了一盆冷水——当 AI 开始真正参与生产环境开发时,事情可能远比想象复杂。 最近,多家媒体披露,本周二亚马逊内部紧急召开了一场工程“深度复盘(deep dive)”会议,专门讨论最近频繁出现的系统故障——其中,一个被反复提及的关键词是:AI 辅助代码。 一周 4 次严重事故,亚马逊内部紧急复盘 事情的起点,是最近一段时间亚马逊系统稳定性明显下降。 负责亚马逊网站技术架构的高级副总裁 Dave Treadwell 在一封内部邮件中坦言:“各位,正如大家可能已经知道的,最近网站及相关基础设施的可用性确实不太理想。” 为此,公司决定把原本每周例行举行的技术会议

By Ne0inhk
这回真的“装”到了!来OpenClaw全国纵深行,你只需要带一台电脑……

这回真的“装”到了!来OpenClaw全国纵深行,你只需要带一台电脑……

AI Agent 的风,已经从 GitHub 吹到了线下。 过去几个月,越来越多开发者开始讨论一个问题: 当 AI 不再只是聊天,而是可以执行任务,软件会变成什么样? 在这股浪潮中,一个开源项目迅速进入开发者视野——OpenClaw,在 GitHub 上获得大量关注,相关教程、实践案例不断出现。有人用它自动整理资料,有人用它管理开发流程,还有人尝试让它执行复杂的工作流。 很多开发者第一次意识到: AI 不只是工具,它可能成为“执行者”。 不过,在技术社区之外,大多数人对 Agent 的理解仍停留在概念层面。 * AI Agent 到底是什么? * 如何在自己的电脑上运行? * 普通开发者能否真正用起来? 带着这些问题,一场围绕 OpenClaw 的开发者城市行动正在展开。 ZEEKLOG 发起的OpenClaw 全国纵深行将走进 20 个城市,用最直接的方式回答一个问题——如果

By Ne0inhk