上一篇:C++项目:基于boost在线文档实现的搜索引擎(二)
github: https://github.com/duchenlong/boost-search-engine
在之前的索引模块中,我们对所有的文档进行了编号,建立的正排索引与倒排索引,分别使用vector
数组,以及unordered_map
哈希表存储。也就是我们完成了对已有文件建立索引的过程。
这里,我们需要做的就是搜索的模块
了,也就是对输入的文本进行分词,并查找其所在那个文件的可能,来找到可能包含搜索内容的文本,并在前端中显示。
同理,作为一个大的模块,我们也需要对这个模块进行封装,方便我们后面进行调用(这也是面向对象的三大特征之一,哈)
// 搜索模块
class Searcher {
public:
Searcher()
: index(new Index())
{}
// 初始化,构建指定文档的索引
bool Init(const string& input_path);
// 指定文本进行搜索
bool Search(const string& query,string* output);
private:
//得到关键字前后的数据,在前端页面显示的文本
string GetShowContent(const string& content,const string& word);
private:
//需要索引进行搜索
Index* index;
};
这里我们采用的是组合
的方式将两个类连接起来,而不是继承。
将索引的模块组合到搜索模块中,那么我们在后面调用的时候就能简单一点了,不用再声明索引模块,再调用索引的方法,使用在搜索中封装好的方法,还能省事
这里的初始化,也就相当于是我们对索引模块的一次有效的封装,直接建立好了所有文档的倒排索引与正排索引,方便后面调用。
// 初始化,构建指定文档的索引
bool Searcher::Init(const string& input_path){
return index->Build(input_path);
}
unordered_map >
的特性,根据每个关键词,就可以得到所有倒排索引的节点,进而得到了每个文档的权重,内容相似度
,权重越大,也就越可能是想要的文档 // 指定文本进行搜索
bool Searcher::Search(const string& query,string* output){
// 分词
vector<string> words;
index->CutWord(query,&words);
// 触发,根据分词的结果,进行倒排索引,得到相关文档
vector<backwardIdx> wordsResult;
for(string word : words){
boost::to_lower(word);
//const vector*
auto* backList = index->GetBackwardIdx(word);
if(backList == nullptr){
// 没有这个关键词
continue;
}
//插入多个数据
wordsResult.insert(wordsResult.end(),backList->begin(),backList->end());
}
if(wordsResult.size() == 0){
// 没有与内容相关数据 404 Not Fount
return false;
}
// 排序
std::sort(wordsResult.begin(),wordsResult.end(),
[](const backwardIdx& le,const backwardIdx& ri){
return le._weight > ri._weight;
});
// 包装
Json::Value value;
for(const auto& backidx : wordsResult){
// 根据 id 查找正排索引
const frontIdx* doc_info = index->GetFrontIdx(backidx._docId);
Json::Value tmp;
tmp["title"] = doc_info->_title;
tmp["url"] = doc_info->_url;
tmp["desc"] = GetShowContent(doc_info->_content,backidx._word);
value.append(tmp);
}
Json::FastWriter writer;
*output = writer.write(value);
return true;
}
获得前端需要显示的文本函数
//得到关键字前后的数据,在前端页面显示的文本
string Searcher::GetShowContent(const string& content,const string& word){
size_t idx = content.find(word);
string ans("");
int pos = 0; // 显示文本开始的位置
int len = lengthText; // 截取显示文本的长度
if(idx == string::npos){
//关键字不存在
len = std::min(len,(int)content.size());
}else {
pos = std::max(0,(int)((int)idx-lengthText/2));
len = std::min((int)lengthText/2,(int)(content.size() - idx));
}
ans = content.substr(pos,len);
ans += "...";
return ans;
}
#include "searcher.hpp"
#include
int main() {
searcher::Searcher searcher;
bool ret = searcher.Init("../data/tmp/raw_input.txt");
if (!ret) {
std::cout << "Searcher 初始化失败" << std::endl;
return 1;
}
while (true) {
std::cout << "searcher> " << std::flush;
string query;
std::cin >> query;
if (!std::cin.good()) {
std::cout << "goodbye" << std::endl;
break;
}
string results;
searcher.Search(query, &results);
std::cout << results << std::endl;
}
return 0;
}
服务器的搭建,就使用C++中的httplib
就可以了
对于httplib搭建得服务器中,这个项目主要使用的是从后端获取数据,也就是get
方法
404 Not found
的错误了而在前端页面中,所使用的技术主要就是vue
和jQuery中的ajax
.
const string g_input_path = "../data/tmp/raw_input.txt";
const string g_root_path = "./www";
searcher::Searcher search;
void GetWebData(const httplib::Request& req,httplib::Response& resp){
if(!req.has_param("query")){
resp.set_content("请求参数错误","text/plain;charset=utf-8");
return ;
}
string query = req.get_param_value("query");
string results;
search.Search(query,&results);
resp.set_content(results,"application/json;charset=utf-8");
cout<<results<<endl;
}
int main(){
using namespace httplib;
bool ret = search.Init(g_input_path);
if(ret == false){
cout<<"searcher init error"<<endl;
return 1;
}
Server server;
server.set_base_dir(g_root_path.c_str());
server.Get("/searcher",GetWebData);
server.listen("0.0.0.0",19998);
return 0;
}
找到指定内容时