《C/C+++ Boost 轻量级搜索引擎实战:架构流程、技术栈与工程落地指南——构造正/倒排索引(中篇)》

《C/C+++ Boost 轻量级搜索引擎实战:架构流程、技术栈与工程落地指南——构造正/倒排索引(中篇)》
前引:这是一个聚焦基础搜索引擎核心工作流的实操项目,基于 C/C++ 技术生态落地:从全网爬虫抓取网页资源,到服务器端完成 “去标签 - 数据清洗 - 索引构建” 的预处理,再通过 HTTP 服务接收客户端请求、检索索引并拼接结果页返回 —— 完整覆盖了轻量级搜索引擎的端到端逻辑。项目采用 C++11、STL、Boost 等核心技术栈,搭配 CentOS 7 云服务器 + GCC 编译环境(或 VS 系列开发工具)部署,既适配后端工程的性能需求,也能通过可选的前端技术(HTML5/JS 等)优化用户交互,是理解搜索引擎底层原理与 C++ 工程实践的典型案例

目录

【一】Jieba分词工具

【二】正/倒排索引结构设计

【三】关键函数设计

(1)由文档ID返回文档内容

(2)由关键字返回倒排拉链

(3)说明

(4)建立索引

(5)建立正排索引

(6)建立倒排索引

【四】单例模式


【一】Jieba分词工具

我们在对使用倒排索引的时候需要用到“关键词”,这个关键词由每个.html文档的标题和内容而来,因此就涉及到分词,所以我们使用 cppjieba 分词工具来完成,下面是上传:

如果需要使用到cppjieba分词工具,可以在gitcode上面直接搜索使用Git命令上传,我这里是本地上传到服务器:

然后对 cppjieba/include/cppjieba和cppjieba/dict分别建立软链接:头文件和词库

我们把cppjieba移动到上级目录,然后更新一下这两个软链接:

【二】正/倒排索引结构设计

//正排结构 typedef struct Forward_index { //文档内容 std::string title; std::string source; std::string chain; //对应正排ID uint64_t doc_id; }Forwardindex; //倒排结构 typedef struct Inverted_index { //对应正排ID int doc_id; //涉及到的关键字 std::string word; //权重 int weight; }Invertedindex; 

正排:根据ID映射对应的文档(标题、内容、URL),这个ID刚好利用vector的下标!

倒排:根据“关键字”映射对应的ID,所以利用unordered_map快速的搜索特性

//正排存储 std::vector<Forwardindex> Forward; typedef std::vector<Invertedindex> Stock_Inverted; //倒排存储 std::unordered_map<std::string,Stock_Inverted> Inverted;

【三】关键函数设计

(1)由文档ID返回文档内容

含义:即正排外部的实现,根据ID返回vector中对应的具体内容

//根据ID返回文档内容 Forwardindex* GetForward_index(const long long& id) { if(id>=Forward.size()) { std::cerr<<"GetForward_index is errno"<<std::endl; return nullptr; } return &Forward[id]; }
(2)由关键字返回倒排拉链

含义:倒排外部实现,根据“关键字”返回对应的ID,即返回对应的vector<Invertedindex>成员

解释:每个关键词涉及到的ID肯定不止一个,所以需要vector<Invertedindex>代表一个关键字涉               及到的所有文档

//根据关键字返回倒排拉链 Stock_Inverted* GetInverted_index(const std::string word) { auto it =Inverted.find(word); if(it == Inverted.end()) { std::cerr<<"GetInverted_index is errno"<<std::endl; return {}; } return &it->second; }
(3)说明

现在我们已经有了给外部的接口:由ID返回文档内容、由关键词返回文档ID

那么接下来就是填变量、建立索引关系:

//正排存储 std::vector<Forwardindex> Forward; typedef std::vector<Invertedindex> Stock_Inverted; //倒排存储 std::unordered_map<std::string,Stock_Inverted> Inverted;
(4)建立索引

原理:利用上一篇文章完成的数据清洗,最终每个.html文档内容都被输出到了data_clean.txt中

我们先利用getline按行读取(也就是按单个文档读取,因为data_clean.txt中也是按行写的)到 line变量里面,这样就拿到了单个文档的内容,因此while就是拿取data_clean.txt的全部文档内容!

建索引也就是同时建立正排和倒排的内容,也就是填vector和Unordered_map的操作!

//建立索引 bool Buildindex(const std::string &input) { std::ifstream in(input,std::ios::in | std::ios::binary); if(!in.is_open()) { std::cerr<<"Buildindex is errno"<<std::endl; return false; } //存单个读取文档内容 std::string line; while(std::getline(in,line)) { //正排 Forwardindex * doc = BUild_Forward_Index(line); printf("正在建立索引:%lld\ntitle:%s\nchain:%s\n",doc->doc_id,doc->title.c_str(),doc->chain.c_str()); if(doc==nullptr)continue; //倒排 Build_Inverted_Index(*doc); } return true; }
(5)建立正排索引

思路:首先我们拿到了单个文档的内容,也就是 line(string)变量,然后对这个内容进行解析,提取出标题、内容、URL(每个line变量都是:标题 | 内容 | URL 的格式,分割即可!)然后填入到vector里面,该文档的ID刚好就是vector的下标,下面有正排的结构体,大家可以对照着看:

//建立正排索引 Forwardindex *BUild_Forward_Index(const std::string&line) { Forwardindex* index = new Forwardindex(); //截取title size_t set_pos1 = line.find('\3'); if(set_pos1 == std::string::npos) { delete index; return nullptr; } index->title = line.substr(0, set_pos1); if (index->title.empty()) index->title = "空"; //截取source size_t set_pos2 = line.find('\3', set_pos1+1); if(set_pos2 == std::string::npos) { delete index; return nullptr; } index->source = line.substr(set_pos1+1, set_pos2 - (set_pos1+1)); if (index->source.empty()) index->source = "空"; //截取URL(chain) index->chain = line.substr(set_pos2+1); if (index->chain.empty()) index->chain = "空"; index->doc_id = Forward.size(); Forward.push_back(*index); return index; }
//正排结构 typedef struct Forward_index { //文档内容 std::string title; std::string source; std::string chain; //对应正排ID uint64_t doc_id; }Forwardindex;
(6)建立倒排索引
//建立倒排索引 bool Build_Inverted_Index(const Forwardindex& doc) { //建立分词对象 JiebaUtil jieba; std::vector<std::string> S; //对标题统计 S=jieba.Tokenize(doc.title); //此时V存放的是每个标题的分词,然后统计结果和出现次数 struct culculate { int title_size=0; int source_size=0; }; std::unordered_map<std::string,culculate> V; //遍历S统计标题的出现次数 for(auto e:S) { (V[e].title_size)++; } //统计内容出现次数 S.clear(); S=jieba.Tokenize(doc.source); for(auto e:S) { (V[e].source_size)++; } //遍历V的结果写入到Inverted for(auto it:V) { Invertedindex index_t; index_t.word=it.first; index_t.doc_id=doc.doc_id; index_t.weight=((it.second.title_size)*2+(it.second.source_size)*1); Inverted[it.first].push_back(std::move(index_t)); } return true; }

【四】单例模式

单例模式:只允许创建一个Index对象,后面只能由 index 指针调用该对象

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; class Index { public: typedef std::vector<Invertedindex> Stock_Inverted; //禁止拷贝构造和赋值重载 Index(const Index&)=delete; Index& operator=(const Index&)=delete; static Index*handle() { if(instance==nullptr) { pthread_mutex_lock(&mutex); if (instance == nullptr) { instance = new Index; } pthread_mutex_unlock(&mutex); } return instance; } ...... }
 //静态成员初始化 Index* Index::instance=nullptr;

Read more

Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 当大模型能在几秒钟内生成一段“看起来像那么回事”的补丁时,开源社区却开始付出另一种代价。 最近,开源游戏引擎 Godot 的核心维护团队公开吐槽:他们正被大量“AI 生成的低质量代码”淹没。那些代码往往结构完整、注释齐全、描述洋洋洒洒,但真正的问题是——提交者可能并不理解自己交上来的内容。 这件事,并不是简单的“有人偷懒用 AI 写代码”。它正在触及开源协作最核心的东西:信任。 一场悄无声息的“AI 洪水” 事情的导火索来自一条 Bluesky 讨论帖。 Godot 主要维护者之一、同时也是 Godot 商业支持公司 W4 Games 联合创始人的 Rémi Verschelde 表示,所谓的“AI slop”

By Ne0inhk
诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

当宇宙级的“嘴炮”遇到降维打击。 编译 | 王启隆 来源 | youtu.be/l6ZcFa8pybE 出品丨AI 科技大本营(ID:rgznai100) 打开最新一期知名播客 StarTalk 的 YouTube 评论区,最高赞的一条留言是这样写的: “我长这么大,第一次看到尼尔·德葛司·泰森(Neil deGrasse Tyson)在一档节目里几乎全程闭嘴,像个手足无措的小学生一样乖乖听讲。” 作为全美最知名的天体物理学家,泰森平时的画风是充满激情、喋喋不休、用宇宙的宏大来震撼嘉宾。但这一次,坐在他对面的那位满头银发、带着温和英音的英国老人,仅仅用最平淡的语气,就让整个演播室陷入了数次令人窒息的沉默。 这位老人是 Geoffrey Hinton。深度学习三巨头之一,2024 年诺贝尔物理学奖得主,被公认为“AI 教父”。 对经常阅读 Hinton 演讲的我来说,这也是比较新奇的一幕—

By Ne0inhk
48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 「仅过了 48 小时,一笔 8.2 万美元的天价费用凭空出现,较这家小型初创公司的正常月费暴涨近 46000%。」 这不是假设的虚幻故事,而是一家墨西哥初创公司正在经历的真实危机。 近日,一位名为 RatonVaquero 的开发者在 Reddit 发帖求助称,由于他的 Gemini API 密钥被盗用,原本每月仅约 180 美元(约 1242 元)的费用,在短短 48 小时内暴涨到 82,314.44 美元(约 56.8 万元)。对于这家只有三名开发者的小型创业团队来说,这笔突如其来的账单,几乎等同于灭顶之灾。 “我现在整个人都处在震惊和恐慌之中。”RatonVaquero

By Ne0inhk
假网站排全网第二,真官网翻五页都找不到!NanoClaw创始人破防:SEO之战,我快要输了

假网站排全网第二,真官网翻五页都找不到!NanoClaw创始人破防:SEO之战,我快要输了

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 自从 OpenClaw 爆火之后,各种“Claw”项目接连出现,其中以安全优化版 NanoClaw 最为知名。它的核心代码仅有 4000 行,却获得了 AI 大牛 Andrej Karpathy 的点赞。 可谁也没想到,这款口碑极佳的开源项目,近来竟被一个仿冒网站抢了风头。 投诉无门之下,NanoClaw 创始人 Gavriel Cohen 在 X 社交平台上无奈发文怒斥:谷歌搜索错误地将假网站排在真官网前面,不仅破坏了项目声誉,还埋下了严重的安全隐患,而他费尽心力,却只能哀叹一句——“我正在为自己的开源项目打 SEO 战,但我快要输了。” 那么,NanoClaw 究竟发生了什么?又是怎么走红的?事情还要从 OpenClaw

By Ne0inhk