【项目】基于正倒排索引的Boost搜索引擎

项目开源代码分享:boost_searcher

可能会被问到的面试内容:

  1. 为什么要做这个项目?/ 项目背景

    主要是日常生活中经常使用搜索引擎,对于搜索引擎技术是如何实现的很感兴趣,再加上很想锻炼自己的C++工程代码能力,于是就在github上找到了这个开源项目,所以就选择开始做这个项目

  2. 介绍一下项目流程和架构设计

    (先说一下项目背景)

    项目主要分成下列模块:数据清洗模块,对boost的所有html文档进行解析,去除标签,提取核心数据,所有文档内容都在output普通文件中,\n作为每个文档的分隔符;索引模块,构建正排索引和倒排索引;搜索模块,基于索引,按照查询词查找出匹配的html文档;web模块,基于cpp-httplib搭建http服务器,提供http接口,编写前端页面。

(不是重点)数据清洗模块具体步骤:第一步,遍历boost的每个html文档,它们的 文件名+路径 保存在files_list中,方便后期对文档进行一个一个的读取。第二步,从files_list中读取每个文件的内容并进行解析,解析成一个一个的DocInfo_t结构体,结构体内部是文档的标题、文档内容、文档在官网的url,结构体放在results里。第三步,把解析完毕的各个文件内容写入到output普通文件中,\n作为每个文档的分隔符。

  1. 制作索引的流程是什么样?

    数据清洗,对boost的所有html文档进行解析,去除标签,提取html中的标题,正文,url。所有文档内容都在output普通文件中,\n作为每个文档的分隔符。构建正排索引,对一个文档的字符串进行拆分,分成3个字符串,再把拆分后的字符串填充到DocInfo,构造成文档对象,通过一个数组管理起来,数组下标就是"文档id",得到文档id=>文档内容的映射。构建倒排索引,针对每个文档对象的标题和正文,进行分词和词频统计,分词结果作为key,文档id列表作为value,添加到一个哈希表中,得到词=>文档id列表的映射。在构建倒排过程中,在该文档中分别统计每个分词在标题和正文中出现的次数,作为后续排序依据(权重)

  2. 详细描述一下检索Search的流程?

    分词,对用户输入的查询词进行分词;触发,根据分词结果,查倒排索引,获取文档id列表;归并,针对多个词触发的文档id进行归并,把相同的id的触发结果合并成一个,并提高权重;排序,根据权重的值降序排序;包装结果,根据文档id,去正排索引中查找,生成描述,最终构造出完整的返回结果

  3. http服务器是如何搭建的?

    这部分我没有自己实现,因为自己主要以业务为导向,为了节省开发时间所以直接选用成熟的开源库,虽然没有从头实现,但事先对候选库做了充分调研。我调研了httplib、mongoose、httpd等轻量http库,最终选择httplib,原因如下:httplib是单头文件实现的,克隆仓库后包含一个头文件即可,无需额外编译链接;httplib是基于select或其他多路转接技术管理IO事件,配合线程池处理就绪连接;httplib内部维护URI正则和回调函数的映射表,只需实现业务处理函数并注册到对应路径,十几行代码即可完成服务器搭建;项目基于C++编写,httplib使用C++实现,集成比C语言的httpd更自然,相比同样为C++的mongoose,httplib使用门槛更低

6.搜索结果是如何排序的?

在搜索结果排序上,我采用了基于词频加权的相关性打分策略。构建倒排索引时,对每个文档分别统计每个分词在标题和正文中出现的次数,并按照"标题权重系数10、正文权重系数1"计算该词在当前文档中的权重值。用户查询时,先将查询语句分词,再按照这些分词查找每个词的倒排拉链;如果同一个文档命中了多个查询词,则将这些词对应的权重相加得到文档的总分。最终,所有文档按总分降序排列返回给用户,总分越高的文档被认为与查询越相关

  1. 你在项目中遇到了哪些挑战?你是如何解决这些问题的?
    1.在构建索引前,需要提取分散在多层子目录下的html文档,若手动用opendir/readdir递归遍历,代码会非常冗长,且容易出错,同时还要高效过滤出html文件,避免把无关文件读入内存。
    解决:我使用了Boost库中的filesystem模块,特别是recursion_directory_iterator。这个迭代器会自动完成深度优先的目录递归,每步都能拿到完整的path对象。我只需检查当前项是否为普通文件,再通过extension()判断后缀是否为.html,将符合条件的绝对路径存入files_list里。这样可以把十几行的递归逻辑压缩为几行循环,还保证了代码在Windows/Linux上都能正确运行。

2.从html中提取去除标签的纯净文本及标题、url。html包含、<div>等标签,直接保存会干扰索引的准确性。<br/> 解决:去标签,实现简易状态机(LABLE/CONTENT),遇到>进入内容区,遇到<退回标签区,只保留文本字符并过滤换行符(\n作为每个文档的分隔符,所以一个文档内部不能有\n)。提取标题用find("<title>")和find("")截取中间内容。构建url,官网固定前缀+相对路径拼接。

3.用户搜索可能中英文混合,且需忽略英文大小写差异;中文必须用词典分词。

解决:我使用了cppjieba库进行混合分词,对分词后的每个词调用boost::to_lower统一转小写,保证大小写命中同一个倒排拉链,在构建倒排索引和搜索时都进行相同的小写化处理。

4.正排索引用vector,倒排用unordered_map<string, InvertedList>,需保证全局单例且并发构建索引安全。

解决:使用单例模式+双重检查锁(mutex)确保Index对象全局唯一,所有词频统计、加权操作都在内存中完成,不依赖外部数据库。

5.格式的统一。

解决:搜索时用Json::FastWriter将结果序列化为JSON字符串,通过httplib库返回给HTTP客户端。

6.参考8

8.例如一次输入两个词A和B,如果A和B的倒排拉链中都包含同一个文档,此时如何处理?

这样的文档包含多个查询词,相关性理解成更高,应该要排到更靠前的位置。

做法就是针对同一个文档进行权重合并。比如遍历A的倒排拉链,把这里的文档信息记录到一个哈希表中。然后再遍历B的倒排拉链,依次在A中查询。如果发现存在相同的文档,则把两个文档的权重进行相加,并且在最终合并多个拉链时进行去重。

  1. 项目性能如何?有没有尝试优化过性能呢?

    项目的性能主要瓶颈在离线索引构建阶段。我通过添加日志分段计时,发现数据清洗(HTML去标签)和Jieba分词这两个环节占用了超过80%的总时间。针对这个瓶颈,我引入了多线程并发处理:将HTML文件分发给多个工作线程,每个线程独立完成解析、分词和局部索引构建,最后进行索引合并。优化后,处理同样规模的文档集,索引构建总时间从原来的80多秒缩短到了27秒左右,性能提升了3倍以上,同时在线搜索阶段由于完全基于内存的倒排索引,平均响应时间保持在10毫秒以内。

  2. 这个项目为啥和上一个同学的项目一样(主题/功能)?

    因为这个项目本身就是开源项目,网上一搜就会出现好多相关博客,优质博客就那几篇,可能我们参考了一样的博客分析吧

  3. 项目最终成果如何?你从这个项目中学到了什么?

    通过浏览器访问服务器获取搜索页面,输入关键字进行搜索,能够达成针对boost文档的搜索预期结果。通过这个项目,我进一步了解了搜索引擎的工作原理,对Linux、HTTP、数据结构等核心操作有了进一步的提高,锻炼了项目设计、解决问题的能力。

  4. 你在这个项目中使用了哪些技术?这些技术是怎么选型的,对比过其他的相关技术吗?

    STL:C++常用技术;cpp-httplib:非常轻量的http库,相比于cpp-netlib来说,cpp-httplib引入的依赖更少,无需编译,直接引入头文件就可以使用,并且官方文档简洁清晰,非常快速就能上手;cppjieba:是一个分词库,在开源的分词库中,cppjieba分词效果较好,足够满足本项目的需求。

  5. 你在项目中扮演了什么角色?你负责了哪些模块或功能?

    我自己独立完成项目的设计、实现、测试、部署

  6. 在项目中,你是如何平衡技术实现和项目时间线的?

    这个项目我花了2个月的时间来完成,从项目目标的确定,到项目原型图的敲定,到各个模块的拆分,再到最后的开发测试部署。关于时间,其实我没有制定详细的计划,是因为我也是抱着学习的心态来完成这个项目,在开发中可能会遇到一些问题,解决问题也是我学习的过程,因此时间就不能确定下来。

  7. 你是如何测试和部署这个项目的?

    测试参考"测试课程-测试用例设计"。部署:构建自动化,通过脚本和Makefile来进行项目构建和程序部署;环境准备,确保部署环境(开发、生产)都已正确设置;部署,将包发布在阿里云服务器并后台启动;日志,部署后打印日志以便于问题诊断。

  8. 你是如何设计项目以支持未来的扩展?你是如何确保项目代码的可维护性的?

    当前是针对boost文档进行解析和制作索引的,后续也可以引入其他的html文档,做更丰富的搜索功能

  9. 正排索引和倒排索引的结构是什么样子?举例说明

    正排索引:根据文档id找到文档内容

倒排索引:根据 关键词 找到 文档id列表(倒排拉链)

  1. 你是如何实现的分词?分词的原理是?

    分词的原理是基于词典的分词,中文词汇数量庞大,但是对于计算机来说是可以穷尽枚举的;我是直接使用第三方库cppjieba进行分词,并未关注具体分词算法的实现

  2. 词和文档的相关性是如何衡量的?

    通过词频衡量,使用的公式是 标题中出现的次数 * 10 + 正文中出现的次数 来作为权重。

相关推荐
Keano Reurink9 小时前
SEO数据管道:用Airflow搭建自动化工作流
运维·人工智能·爬虫·搜索引擎·自动化·ai编程·seo
Elastic 中国社区官方博客12 小时前
一个查询,无限 Elasticsearch Serverless 项目:跨项目搜索介绍
大数据·elasticsearch·搜索引擎·信息可视化·云原生·serverless·全文检索
qq_2965532712 小时前
[特殊字符] 旋转排序数组中的高效搜索:从线性到二分查找的进阶之路
数据结构·算法·搜索引擎·分类·柔性数组
Keano Reurink14 小时前
搜索API驱动的竞品监控:7×24小时跟踪对手一举一动
人工智能·搜索引擎·dreamweaver
逸Y 仙X16 小时前
文章三:Elasticsearch 集群恢复和索引分布
java·大数据·linux·服务器·elasticsearch·搜索引擎·全文检索
kekekka1 天前
重塑品牌增长逻辑:专业媒体发稿服务如何让每一分预算产生长效复利?
大数据·搜索引擎·媒体
老陈头聊SEO1 天前
长尾关键词助力扫描SEO效果的全新方法
其他·搜索引擎·seo优化
深念Y2 天前
Claude Code 搜索工具失灵,用 MCP + 提示词注入绕过 tavily
网络·搜索引擎·mcp·claudecode·中转站·tavily·搜索服务器
IT飞牛2 天前
Elasticsearch 技术调研与实践
大数据·elasticsearch·搜索引擎