C++项目:基于boost在线文档实现的搜索引擎(三)

C++项目:基于boost在线文档实现的搜索引擎(三)

上一篇: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<string,vector<backwardIdx> >的特性,根据每个关键词,就可以得到所有倒排索引的节点,进而得到了每个文档的权重,内容
  • 排序:不同关键词的权重,就代表了这个关键词所找的的文档和需要搜索的文档的相似度,权重越大,也就越可能是想要的文档
  • 包装:最后我们这些东西都是需要显示到前端页面中的,但是在页面中我们不可能直接把每个文档都展开,那样不美并且太多了。所以我们抽取关键词周围指定大小的字符,然后显示到前端就可以了

在这里插入图片描述

    //  指定文本进行搜索
    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<backwardIdx>*
            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 <ostream>

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的错误了

而在前端页面中,所使用的技术主要就是vuejQuery中的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;
}

初始界面:
在这里插入图片描述

找不到搜索内容时
在这里插入图片描述

找到指定内容时
在这里插入图片描述

还可以点击下一页和上一页
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值