Boost搜索引擎

一、项目背景

公司:百度、搜狗、360搜索、头条新闻客户端 - 我们自己实现是不可能的!
实现站内搜索:搜索的数据更垂直,数据量其实更小

二、搜索引擎相关宏观原理

用户通过客户端(例如浏览器,应用程序)上传搜索关键字,客户端以HTTP请求方式进行搜索任务。服务端主机根据关键字通过已经建立好的索引(如果是全网搜索,搜索引擎会定期向全网网站进行爬虫,建立和更新索引;对于站内搜索,则时可以在服务启动前就建立好索引)进行检索得到相关html,拼接多个网页的 title + desc + url,构建一个新的网页,返回给用户。

三、技术栈和项目环境

技术栈: C/C++ C++11, STL, 准标准库Boost,Jsoncpp,cppjieba,cpp-httplib ,
选学: html5,css,js、jQuery、Ajax

项目环境: Centos 7云服务器,vim/gcc(g++)/Makefile , vs2019 or vs code

四、正排索引 和 倒排索引 - 搜索引擎具体原理

文档1: 雷军买了四斤小米
文档2: 雷军发布了小米手机

正排索引:文档id 与 正文 映射关系

倒排索引:关键字 与 文档id,权重 映射关系

模拟一次查找的过程:
用户输入:小米 -> 倒排索引中查找 -> 提取出文档ID(1,2) -> 根据正排索引 -> 找到文档的内容 ->title+conent(desc)+url 文档结果进行摘要-> 构建响应结果

五、外部库使用

5.1 boost文件系统

boost文件系统filesystem使用:boost::filesystem 暂时取别名为fs

1)fs::recursive_directory_iterator 递归迭代器,默认构造可以代表end,传值构造的值要用目录。作用:递归式地遍历目录下所有的文件(包括目录)

2)fs::is_regular_file(路径),判断是否是常规文件

3)fs::extension(路径),获取拓展名,即文件后缀

4)反向迭代器->path().string(),获取当前路径字符串

5.2 boost切分字符串 和 大小写转换

头文件:<boost/algorithm/string.hpp>
boost::split(*out, target, boost::is_any_of(sep), boost::token_compress_on);

  • std::vector<std::string> *out存放截取后的字符串
  • target为要截取的字符串
  • sep为分隔符,可以是多个
  • token_compress_on表示连续分隔符中间的空串不保留;反之off保留

头文件:<boost/algorithm/string/case_conv.hpp>

to_lower 和 to_upper 大小写转换,会改变源字符串

copy_to_lower 和 copy_to_upper 不会改变源字符串

5.3 cppjieba使用

https://github.com/yanyiwu/cppjieba/archive/refs/tags/v5.0.3.tar.gz

选择使用5.0.3版本
注意事项:将cppjieba-5.0.3/deps/limonp目录拷贝到 cppjieba-5.0.3/include/cppjieba

使用jieba.CutForSearch(string, vector<string>)

将原始str进行切词,放到vector<string>内部

5.4 cpp-httplib库使用

https://github.com/yhirose/cpp-httplib
高版本gcc,添加到启动脚本:~/.bash_profile

特别注意:listen要放在最后面,listen启动

set_base_dir:设置web根目录

Get:设置http请求方法GET,第一个参数uri为"/hi",第二个参数回调std::function<void(const httplib::Request &, httplib::Response &)>

六、整体逻辑梳理

模块一:数据清洗

1)读取所有html文件路径

2)读取文件路径对应内容,进行数据清洗(取标题,去标签留内容,拼接url)

3)根据格式写到文件里

模块二:建立索引

建立单个索引逻辑:

1)根据一行内容,按格式分割出来,插入到正排索引数组,建立正排索引,返回刚插入的正排节点指针

2)对正排对象的title和content分别进行分词,记录到数组中

3)用哈希表,以分词作为key,

struct Cnt

{

size_t titleCnt = 0;

size_t contentCnt = 0

}; 作为value

对title分词和content分词数量分别进行统计(特别地,分词要转成小写

4)遍历哈希表,Index中记录倒排拉链的哈希表(关键字,倒排拉链)用分词.push_back(倒排节点);倒排节点的id填1)正排节点指针对应的id,word填分词,weight = 标题关键字数量*10 + 正文关键字

建立所有索引:循环读取一行数据清洗后的文件结果,将这一行数据给建立单个索引

模块三:搜索模块

初始化逻辑:

1)获取index单例

2)建立index所有索引

搜索逻辑:

1)将query进行分词,结果记录到数组

2)创建umap(文档id,倒排打印节点),遍历分词结果,判断query分词是否有对应的倒排拉链

a.没有,continue

b.有,遍历倒排拉链,用umap倒排节点._id,赋值id,累加相同id的权值,尾插相同id的关键字(目的:query中出现两个及以上的关键字来自同一个文档时,结果会出现多份相同url,为了避免这种情况,使用umap让id作为key值,去重id)

3)将umap中倒排打印节点拷贝到数组中,按照totalWeight进行排序

4)遍历倒排打印节点数组,用id查正排,获得正排节点指针,将其id,分词数组第一个元素content摘要,url用cppjson库序列化成json串

5)输出型参数json_string带出json串

模块四:http服务模块

整体逻辑:

1)建立Searcher对象

2)建立http服务对象

3)设置web根目录,注册HTTP请求方式为Get方式的回调方法:由前端页面提供query字符串,后端调用Searcher对象的搜索方法得到结果json串,将json串返回给前端页面

4)设置监听任意IP,设置端口号,启动监听

细节点:

对于去掉暂停词:

1)建立unordered_set读上来cppjieba的停止词文件

2)将切分词后的结果拷贝到链表中

3)链表去比对停止词来进行删除

4)链表去除停止词后的结果拷贝回结果数组中

注:双向链表任意位置删除时间复杂度O(1),数组任意位置删除时间复杂度O(N),删除用链表可以大大提高建立索引的过程。

七、项目源码

Linux-remote: linux远程仓库https://gitee.com/its-quite-six/linux-remote/tree/master/BoostProject

相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩4 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言