【C++项目】boost搜索引擎

这篇具有很好参考价值的文章主要介绍了【C++项目】boost搜索引擎。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、项目的相关背景

1.1 boost基本介绍

boost官网

Boost库是为C++语言标准库提供扩展的一些C++程序库的总称。

  • Boost库由Boost社区组织开发、维护。其目的是为C++程序员提供免费、同行审查的、可移植的程序库。Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能。Boost库使用Boost License来授权使用,根据该协议,商业的非商业的使用都是允许并鼓励的。
  • Boost社区建立的初衷之一就是为C++的标准化工作提供可供参考的实现,Boost社区的发起人Dawes本人就是C++标准委员会的成员之一。在Boost库的开发中,Boost社区也在这个方向上取得了丰硕的成果。在送审的C++标准库TR1中,有十个Boost库成为标准库的候选方案。在更新的TR2中,有更多的Boost库被加入到其中。从某种意义上来讲,Boost库成为具有实践意义的准标准库。
  • 大部分boost库功能的使用只需包括相应头文件即可,少数(如正则表达式库,文件系统库等)需要链接库。里面有许多具有工业强度的库,如graph库。
  • 很多Boost中的库功能堪称对语言功能的扩展,其构造用尽精巧的手法,不要贸然的花费时间研读。Boost另外一面,比如Graph这样的库则是具有工业强度,结构良好,非常值得研读的精品代码,并且也可以放心的在产品代码中多多利用

1.2 为什么要自主实现boost搜索引擎

  • 百度、搜狗、360搜索、头条新闻客户端 - 我们自己实现是不可能的!(全网搜索)
    【C++项目】boost搜索引擎
    【C++项目】boost搜索引擎
    【C++项目】boost搜索引擎

  • boost的官网是没有站内搜索的,需要我们自己做一个
    【C++项目】boost搜索引擎
    站内搜索:搜索的数据更垂直,数据量其实更小

二、搜索引擎的相关宏观原理和项目演示

【C++项目】boost搜索引擎
用户输入:关键字 -> 倒排索引中查找 -> 提取出文档ID -> 根据正排索引 -> 找到文档的内容 ->title+conent(desc)+url 文档结果进行摘要->构建响应结果

2.1 项目演示:

【C++项目】boost搜索引擎

三、搜索引擎技术栈和项目环境

  • 技术栈: 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

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

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

正排索引:就是从文档ID找到文档内容(文档内的关键字)

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

目标文档进行分词(目的:方便建立倒排索引和查找):

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

停止词:了,的,吗,a,the,一般我们在分词的时候可以不考虑

关键字(具有唯一性) 文档ID
雷军 文档1,文档2
文档1
四斤 文档1
小米 文档1,文档2
四斤小米 文档1
发布 文档2
小米手机 文档2

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

五、编写数据去标签与数据清洗的模块 Parser

目前只需要boost_1_79_0/doc/html目录下的html文件,用它来进行建立索引

【C++项目】boost搜索引擎
【C++项目】boost搜索引擎
【C++项目】boost搜索引擎

#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "Util.hpp"

const std::string src_path = "data/input";
const std::string output = "data/raw_html/raw.txt";

typedef struct DocInfo
{
public:
    std::string title;   // 文档标题
    std::string content; // 文档内容
    std::string url;     // 网址
} DocInfo_t;

// const & 输入
// * 输出
// & 输入输出
bool EnumFile(const std::string &src_path, std::vector<std::string> *file_list);

bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results);

bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output);

int main()
{
    std::vector<std::string> files_list;
    // 第一步,递归式将每个HTML文件名带路径,保存在files_list当中;方便后期一个一个读取
    if (!EnumFile(src_path, &files_list))
    {
        std::cerr << "EnumFile error" << std::endl;
        return 1;
    }
    // 第二步,按照file_list读取每一个文件中的内容,并进行解析
    std::vector<DocInfo_t> results;
    if (!ParseHtml(files_list, &results))
    {
        std::cerr << "ParseHtml error" << std::endl;
        return 2;
    }
    // 第三步,把解析完成的各个文件内容,写入到output里面,按照\n作为每个文档的分隔符 \3作为分割doc里面的各个数据
    if (!SaveHtml(results, output))
    {
        std::cerr << "SaveHtml error" << std::endl;
        return 3;
    }
    return 0;
}

bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    // 判断当前路径是否存在
    if (!fs::exists(root_path))
    {
        std::cerr << src_path << " not exists" << std::endl;
        return false;
    }
    // 定义一个迭代器,来判断递归结束
    fs::recursive_directory_iterator end;
    for (fs::recursive_directory_iterator iter(root_path); iter != end; ++iter)
    {
        // 判断是否是普通文件,HTML是普通文件
        if (!fs::is_regular_file(*iter))
        {
            continue;
        }
        // 判断后缀是否是html
        if (iter->path().extension() != ".html")
        {
            continue;
        }
        // std::cout << "debug:" << iter->path().string() << std::endl;
        //  当前路径一定是合法的,一html为后缀的普通文件
        //将当前路径后缀为HTML的文件名保存在files_list,方便进行文本分析
        files_list->push_back(std::move(iter->path().string())); // move 减少拷贝
    }
    return true;
}

static bool ParseTitle(const std::string &file, std::string *title)
{
    size_t begin = file.find("<title>");
    if (begin == std::string::npos)
    {
        return false;
    }
    size_t end = file.find("</title>");
    if (end == std::string::npos)
    {
        return false;
    }

    begin += std::string("<title>").size(); // begin指向正文
    *title = file.substr(begin, end - begin);
    return true;
}

static bool ParseContent(const std::string &file, std::string *content)
{
    // 去标签,编写一个简单的状态机
    enum status
    {
        LABLE,
        CONTENT
    };

    enum status s = LABLE;
    for (char ch : file)
    {
        switch (s)
        {
        case LABLE:
            if (ch == '>')
            {
                s = CONTENT;
            }
            break;
        case CONTENT:
            if (ch == '<')
            {
                s = LABLE;
            }
            else
            {
                if (ch == '\n')
                {
                    ch = ' ';
                }
                *content += ch;
            }
            break;
        default:
            break;
        }
    }
    return true;
}

static bool ParseUrl(const std::string &file_path, std::string *url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_79_0/doc/html";
    std::string url_tail = file_path.substr(src_path.size());
    *url = url_head + url_tail;
    return true;
}

// for debug
static void ShowDoc(DocInfo_t &doc)
{
    std::cout << doc.title << std::endl;
    std::cout << doc.content << std::endl;
    std::cout << doc.url << std::endl;
}

bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
    for (const std::string &file : files_list)
    {
        // 1,读取文件内容
        std::string result; // 文件内容
        if (!ns_util::FileUtil::ReadFile(file, &result))
        {
            continue;
        }
        DocInfo_t doc;
        // 2.解析指定文件的title
        if (!ParseTitle(result, &doc.title))
        {
            continue;
        }
        // 解析指定文件的content
        if (!ParseContent(result, &doc.content))
        {
            continue;
        }
        // 解析指定文件的url
        if (!ParseUrl(file, &doc.url))
        {
            continue;
        }
        // debug doc
        // ShowDoc(doc);
        // break;
        // 提取完毕,当前文件的相关结果都保存在了doc里面
        results->push_back(doc); // 细节,会发生拷贝
    }
    return true;
}

bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)
{
#define SEP '\3'
    std::ofstream out(output, std::ios::out | std::ios::binary);
    if (!out.is_open())
    {
        std::cerr << "open " << output << " failed" << std::endl;
        return false;
    }
    // 把解析完成的各个文件内容,写入到output里面,按照\n作为每个文档的分隔符 \3作为分割doc里面的各个数据
    for (auto &it : results)
    {
        std::string out_string;
        out_string += it.title;
        out_string += SEP;
        out_string += it.content;
        out_string += SEP;
        out_string += it.url;
        out_string += '\n';
        out.write(out_string.c_str(), out_string.size());
    }
    out.close();
    return true;
}

六、编写建立索引的模块 Index

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include "Util.hpp"
#include <mutex>
#include "Log.hpp"

namespace ns_index
{
    //
    struct DocInfo
    {
        std::string _title;   // 文档标题
        std::string _content; // 文档内容
        std::string _url;     // 文档url
        uint64_t _doc_id;     // 文档id,方便构建倒排拉链
    };

    //倒排拉链
    struct InvertedElem
    {
        uint64_t _doc_id;
        std::string _word;
        int _weight;
    };
    typedef std::vector<InvertedElem> InvertedList;

    class Index
    {
    private:
        Index()
        {
        }
        Index(const Index &) = delete;
        Index &operator=(const Index &) = delete;

    public:
        static Index *GetInstance()
        {
            if (_instance == nullptr)
            {
                std::unique_lock<std::mutex> ulck(_mtx);
                if (_instance == nullptr)
                {
                    _instance = new Index;
                }
            }
            return _instance;
        }

        ~Index()
        {
        }

    public:
        DocInfo *GetForwardIndex(uint64_t doc_id)
        {
            if (doc_id >= _forward_index.size())
            {
                std::cerr << "doc_id out of range" << std::endl;
                return nullptr;
            }
            return &_forward_index[doc_id];
        }

        InvertedList *GetInvertedIndex(const std::string &word)
        {
            auto iter = _inverted_index.find(word);
            if (iter == _inverted_index.end())
            {
                std::cerr << word << "have no Inverted!" << std::endl;
                return nullptr;
            }
            return &(iter->second);
        }

        // parse.cc处理完的数据给我
        // /home/ts/procedure_life/program/boost_sercher/data/raw_html
        bool BuildIndex(const std::string &input)
        {
            std::ifstream in(input, std::ios::in | std::ios::binary);
            std::cout << "file name: " << input << std::endl;
            if (!in.is_open())
            {
                std::cerr << "open " << input << " failed!" << std::endl;
                return false;
            }
            std::string line;
            int count = 0;
            while (std::getline(in, line))
            {
                ++count;
                DocInfo *doc = BuildForwardIndex(line);
                // 构建正排
                if (nullptr == doc)
                {
                    std::cerr << "bulid " << line << " error" << std::endl;
                    continue;
                }
                // 构建倒排
                BuildInvertedIndex(*doc);
                if (count % 50 == 0)
                    // std::cout << "当前正在建立文档:" << count << std::endl;
                    LOG(NORMAL, "构建正排和倒排索引:" + std::to_string(count));
            }

            in.close();
            return true;
        }

    private:
        DocInfo *BuildForwardIndex(const std::string &line)
        {
            // 1.解析line,字符串切分
            std::vector<std::string> results;
            const std::string sep = "\3"; // 行内分隔符
            ns_util::StringUtil::Split(line, &results, sep);
            if (results.size() != 3)
            {
                return nullptr;
            }
            // 2.将字符串填充到DocInfo
            DocInfo doc;
            doc._title = results[0];
            doc._content = results[1];
            doc._url = results[2];
            doc._doc_id = _forward_index.size();
            // 3.插入到正排索引_forward_index中
            _forward_index.push_back(std::move(doc));
            return &_forward_index.back();
        }

        bool BuildInvertedIndex(const DocInfo &doc)
        {
            // title conten url id
            // word -> 倒排拉链

            // 1.对title和content进行jieba分词
            std::vector<std::string> title_words;
            ns_util::JiebaUtil::CutString(doc._title, &title_words);
            std::vector<std::string> content_words;
            ns_util::JiebaUtil::CutString(doc._content, &content_words);

            // 2.统计词频
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;

                word_cnt() : title_cnt(0), content_cnt(0) {}
            };

            std::unordered_map<std::string, word_cnt> word_map;
            // title Hello
            for (auto iter : title_words)
            {
                boost::to_lower(iter);
                word_map[iter].title_cnt++;
            }

            for (auto iter : content_words)
            {
                boost::to_lower(iter);
                word_map[iter].content_cnt++;
            }

            // 3.自定义相关性
#define X 10
#define Y 1
            for (auto &iter : word_map)
            {
                InvertedElem tmp;
                tmp._doc_id = doc._doc_id;
                tmp._word = iter.first;
                tmp._weight = iter.second.title_cnt * X + iter.second.content_cnt * Y;
                InvertedList &inver_list = _inverted_index[iter.first];
                inver_list.push_back(tmp);
            }
            return true;
        }

    private:
        // 正排索引用数组就可以
        std::vector<DocInfo> _forward_index;
        // 倒排索引是关键字和一组InverteLIst的对应  [关键字和倒排拉链的映射]
        std::unordered_map<std::string, InvertedList> _inverted_index;
        static Index *_instance;
        static std::mutex _mtx;
    };
    Index *Index::_instance = nullptr;
    std::mutex Index::_mtx;
}

七、编写搜索引擎模块 Searcher

#pragma once
#include "Index.hpp"
#include "Util.hpp"
#include <algorithm>
#include <jsoncpp/json/json.h>
#include <iterator>
#include "Log.hpp"

namespace ns_sercher
{
    struct InvertedElemPrint
    {
        uint64_t _doc_id = 0;
        int _weight = 0;
        std::vector<std::string> _words;
    };

    class Searcher
    {
    public:
        Searcher() {}
        ~Searcher() {}

    public:
        void InitSearcher(const std::string &input)
        {
            // 1. 获取或则创建index对象
            _index = ns_index::Index::GetInstance();
            // std::cout << "获取单例成功" << std::endl;
            LOG(NORMAL, "获取单例成功...");
            // 2. 根据index对象建立索引
            _index->BuildIndex(input);
            // std::cout << "建立正排和倒排成功" << std::endl;
            LOG(NORMAL, "建立正排和倒排索引成功...");

        }

        void Search(const std::string &query, std::string *json_string)
        {
            // 1.分词,对我们的query进行按照searcher的要求
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query, &words);
            // 2.触发,根据分词的各个词进行index查找
            //ns_index::InvertedList inverted_list_all;
            std::vector<InvertedElemPrint> inverted_list_all;
            std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;

            for (std::string word : words)
            {
                boost::to_lower(word);
                ns_index::InvertedList *inverted_List = _index->GetInvertedIndex(word);
                if (nullptr == inverted_List)
                {
                    continue;
                }
                //inverted_list_all.insert(inverted_list_all.begin(), (*inverted_List).begin(), (*inverted_List).end());
                for(const auto& elem : *inverted_List)
                {
                    InvertedElemPrint& item = tokens_map[elem._doc_id];
                    item._weight += elem._weight;
                    item._doc_id = elem._doc_id;
                    item._words.push_back(elem._word);
                }
            }
            
            for(const auto& item :  tokens_map)
            {
                inverted_list_all.push_back(std::move(item.second));
            }

            // 3.合并排序,汇总查询结果,按照相关性(weight)降序排列
            // std::sort(inverted_list_all.begin(), inverted_list_all.end(), 
            // [](const ns_index::InvertedElem& e1, const ns_index::InvertedElem& e2) {
            //     return e1._weight > e2._weight;
            // });
            std::sort(inverted_list_all.begin(), inverted_list_all.end(), 
            [](const InvertedElemPrint& e1, const InvertedElemPrint& e2){
                return e1._weight > e2._weight;
            });
            // 4.构建,根据查找结果,构建json串,
            Json::Value root;
            for(auto& iter : inverted_list_all)
            {
                ns_index::DocInfo* pdoc = _index->GetForwardIndex(iter._doc_id);
                if(nullptr == pdoc)
                {
                    continue;
                }
                Json::Value elem;
                elem["title"] = pdoc->_title;
                elem["content"] = GetDes(pdoc->_content, iter._words[0]); // 文档是去掉标签后的结果,但是不是我们想要的结果,我们想要的是一部分
                // elem["content"] = pdoc->_content; // 文档是去掉标签后的结果,但是不是我们想要的结果,我们想要的是一部分
                elem["url"] = pdoc->_url;
                //elem["id"] = (int)iter._doc_id;
                //elem["weight"] = iter._weight;
                root.append(elem); 
            }
            Json::FastWriter writer;
            *json_string = writer.write(root);
        }

        std::string GetDes(const std::string& html_content, const std::string& word)
        {
            // 从第一次出现word的位置开始向前找50个字节,向后找100个字节
            const size_t prev_step = 50;
            const size_t next_step = 100;
            // 找到在content中第一次出现word的位置
            auto iter = std::search(html_content.begin(),html_content.end(), word.begin(),word.end(), 
            [](int x, int y){ return std::tolower(x) == std::tolower(y);});
            if(iter == html_content.end())
            {
                return "None1";
            }
            size_t pos = std::distance(html_content.begin(), iter);
            //错误查找
            // size_t pos = html_content.find(word);
            // if(pos == std::string::npos)
            // {
            //     return "None word";
            // }

            size_t start = 0;
            size_t end = html_content.size() - 1;
            if(start + prev_step < pos)
            {
                start = pos - prev_step;
            }
            if(pos + next_step < end)
            {
                end = pos + next_step;
            }
            if(start > end)
            {
                return "None2";
            }
            // 获取start-end之间的字符串
            return html_content.substr(start, end - start);
        }

    private:
        ns_index::Index* _index; // 提供查找的索引
    };
}

八、编写http_server 模块

#include "./cpp-httplib/httplib.h"
#include "Sercher.hpp"
#include "Log.hpp"

const std::string root_path = "./wwwroot";
const std::string input = "data/raw_html/raw.txt";

int main()
{
    httplib::Server svr;
    ns_sercher::Searcher searcher;
    searcher.InitSearcher(input);
    svr.set_base_dir(root_path.c_str());
    svr.Get("/s", [&searcher](const httplib::Request& req, httplib::Response& res){
        if(!req.has_param("word"))
        {
            res.set_content("必须要有搜索关键系!", "text/plain; charset=utf-8");
            return;
        }
        LOG(NORMAL, "搜索关键词成功...");
        std::string word = req.get_param_value("word");
        std::string json_string;
        searcher.Search(word, &json_string);
        res.set_content(json_string, "application/json; charset=utf-8");
        //res.set_content("Hello World!", "text/plain; charset=utf-8");
    });
    LOG(NORMAL, "服务器启动成功...");
    svr.listen("0.0.0.0", 8080);
    return 0;
}

九、编写前端模块

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>

    <title>boost 搜索引擎</title>
    <style>
        /* 去掉网页中的所有的默认内外边距,html的盒子模型 */
        * {
            /* 设置外边距 */
            margin: 0;
            /* 设置内边距 */
            padding: 0;
        }
        /* 将我们的body内的内容100%和html的呈现吻合 */
        html,
        body {
            height: 100%;
        }
        /* 类选择器.container */
        .container {
            /* 设置div的宽度 */
            width: 800px;
            /* 通过设置外边距达到居中对齐的目的 */
            margin: 0px auto;
            /* 设置外边距的上边距,保持元素和网页的上部距离 */
            margin-top: 15px;
        }
        /* 复合选择器,选中container 下的 search */
        .container .search {
            /* 宽度与父标签保持一致 */
            width: 100%;
            /* 高度设置为52px */
            height: 52px;
        }
        /* 先选中input标签, 直接设置标签的属性,先要选中, input:标签选择器*/
        /* input在进行高度设置的时候,没有考虑边框的问题 */
        .container .search input {
            /* 设置left浮动 */
            float: left;
            width: 600px;
            height: 50px;
            /* 设置边框属性:边框的宽度,样式,颜色 */
            border: 1px solid black;
            /* 去掉input输入框的有边框 */
            border-right: none;
            /* 设置内边距,默认文字不要和左侧边框紧挨着 */
            padding-left: 10px;
            /* 设置input内部的字体的颜色和样式 */
            color: #CCC;
            font-size: 14px;
        }
        /* 先选中button标签, 直接设置标签的属性,先要选中, button:标签选择器*/
        .container .search button {
            /* 设置left浮动 */
            float: left;
            width: 150px;
            height: 52px;
            /* 设置button的背景颜色,#4e6ef2 */
            background-color: #4e6ef2;
            /* 设置button中的字体颜色 */
            color: #FFF;
            /* 设置字体的大小 */
            font-size: 19px;
            font-family:Georgia, 'Times New Roman', Times, serif;
        }
        .container .result {
            width: 100%;
        }
        .container .result .item {
            margin-top: 15px;
        }

        .container .result .item a {
            /* 设置为块级元素,单独站一行 */
            display: block;
            /* a标签的下划线去掉 */
            text-decoration: none;
            /* 设置a标签中的文字的字体大小 */
            font-size: 20px;
            /* 设置字体的颜色 */
            color: #4e6ef2;
        }
        .container .result .item a:hover {
            text-decoration: underline;
        }
        .container .result .item p {
            margin-top: 5px;
            font-size: 16px;
            font-family:'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
        }

        .container .result .item i{
            /* 设置为块级元素,单独站一行 */
            display: block;
            /* 取消斜体风格 */
            font-style: normal;
            color: green;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="search">
            <input type="text" value="请输入搜索关键字">
            <button onclick="Search()">搜索一下</button>
        </div>
        <div class="result">
            <!-- 动态生成网页内容 -->
            <!-- <div class="item">
                <a href="#">这是标题</a>
                <p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p>
                <i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i>
            </div>
            <div class="item">
                <a href="#">这是标题</a>
                <p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p>
                <i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i>
            </div>
            <div class="item">
                <a href="#">这是标题</a>
                <p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p>
                <i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i>
            </div>
            <div class="item">
                <a href="#">这是标题</a>
                <p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p>
                <i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i>
            </div>
            <div class="item">
                <a href="#">这是标题</a>
                <p>这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要这是摘要</p>
                <i>https://search.gitee.com/?skin=rec&type=repository&q=cpp-httplib</i>
            </div> -->
        </div>
    </div>
    <script>
        function Search(){
            // 是浏览器的一个弹出框
            // alert("hello js!");
            // 1. 提取数据, $可以理解成就是JQuery的别称
            let query = $(".container .search input").val();
            console.log("query = " + query); //console是浏览器的对话框,可以用来进行查看js数据

            //2. 发起http请求,ajax: 属于一个和后端进行数据交互的函数,JQuery中的
            $.ajax({
                type: "GET",
                url: "/s?word=" + query,
                success: function(data){
                    console.log(data);
                    BuildHtml(data);
                }
            });
        }

        function BuildHtml(data){
            // 获取html中的result标签
            let result_lable = $(".container .result");
            // 清空历史搜索结果
            result_lable.empty();

            for( let elem of data){
                // console.log(elem.title);
                // console.log(elem.url);
                let a_lable = $("<a>", {
                    text: elem.title,
                    href: elem.url,
                    // 跳转到新的页面
                    target: "_blank"
                });
                let p_lable = $("<p>", {
                    text: elem.desc
                });
                let i_lable = $("<i>", {
                    text: elem.url
                });
                let div_lable = $("<div>", {
                    class: "item"
                });
                a_lable.appendTo(div_lable);
                p_lable.appendTo(div_lable);
                i_lable.appendTo(div_lable);
                div_lable.appendTo(result_lable);
            }
        }
    </script>
</body>
</html>

十、添加日志

#pragma once

#include <iostream>
#include <string>
#include <ctime>

#define NORMAL 1
#define WARNING 2
#define DEBUG 3
#define FATAL 4

#define LOG(LEVEL, MESSAGE) Log(#LEVEL, MESSAGE, __FILE__, __LINE__)

void Log(const std::string& level, const std::string& message, const std::string& file, int line)
{
    std::cout << "[level:" << level << "]" << "[time:" << time(nullptr) << "]" << "[message:" << message << "]"
    << "[file:" << file << "]" << "[line:" << line << "]" << std::endl;
}

10.1 部署服务到 linux 上

nohup ./http_server > log/log.txt 2>&1 &

十一、结项总结

项目扩展方向文章来源地址https://www.toymoban.com/news/detail-414662.html

  1. 建立整站搜索
  2. 设计一个在线更新的方案,信号,爬虫,完成整个服务器的设计
  3. 不使用组件,而是自己设计一下对应的各种方案(有时间,有精力)
  4. 在我们的搜索引擎中,添加竞价排名(强烈推荐)
  5. 热次统计,智能显示搜索关键词(字典树,优先级队列)(比较推荐)
  6. 设置登陆注册,引入对mysql的使用(比较推荐的)

到了这里,关于【C++项目】boost搜索引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Boost搜索引擎项目

    目录 1.项目相关背景 2.搜索引擎的相关宏观原理 3.搜索引擎技术栈和项目环境 4.正排索引 倒排索引--搜索引擎原理 5.编写数据去标签与数据清洗的模块 -- parser.hpp 去标签 编写parser(将文件去标签) 编写EnumFile函数 编写ParseFile函数 解析三大部分: 编写SaveFile函数 6.建立索引--编写

    2024年02月01日
    浏览(82)
  • 【项目】Boost搜索引擎

    研发搜索引擎的公司,如百度、搜狗、360搜索,还有各大网站各种客户端也提供搜索功能 为什么选择实现Boost搜索引擎 1)因为Boost官方网站是没有搜索功能的,所以我们可以为Boost实现一个站内搜索引擎,虽然官方提供了boost相关的一些方法,标准库中的一些接口,但是我们想看到

    2024年02月03日
    浏览(52)
  • [C++项目] Boost文档 站内搜索引擎(3): 建立文档及其关键字的正排 倒排索引、jieba库的安装与使用...

    之前的两篇文章: 第一篇文章介绍了本项目的背景, 获取了 Boost 库文档 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 第二篇文章 分析实现了 parser 模块. 此模块的作用是 对所有文档 html 文件, 进行清理并汇总 🫦[C++项目] Boost文档 站内搜

    2024年02月07日
    浏览(58)
  • [C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对文档文件去标签、如何获取文档标题...

    在上一篇文章中, 已经从 Boost 官网获取了 Boost 库的源码. 相关文章: 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 接下来就要编写代码了. 不过还需要做一些准备工作. 创建项目目录 所有的项目文件肯定要在一个目录下, 找一个位置执行下

    2024年02月14日
    浏览(46)
  • 基于boost库的搜索引擎项目

    boost库是指一些为C++标准库提供扩展的程序库总称,但是boost网站中并没有为我们提供站内搜索功能,因此我们要想找到某一个类的用法还要一个个去找,因此我们这次的目的就是实现一个搜索引擎功能,提高我们获取知识的效率 比如百度,谷歌,360等,这些都是大型的搜索

    2024年03月14日
    浏览(90)
  • 基于boost准标准库的搜索引擎项目

    这是一个基于Web的搜索服务架构 客户端-服务器模型 :采用了经典的客户端-服务器模型,用户通过客户端与服务器交互,有助于集中管理和分散计算。 简单的用户界面 :客户端似乎很简洁,用户通过简单的HTTP请求与服务端交互,易于用户操作。 搜索引擎功能 :服务器端的

    2024年04月27日
    浏览(36)
  • 【Boost搜索引擎项目】Day1 项目介绍+去标签和数据清洗框架搭建

    🌈欢迎来到C++项目专栏 🙋🏾‍♀️作者介绍:前PLA队员 目前是一名普通本科大三的软件工程专业学生 🌏IP坐标:湖北武汉 🍉 目前技术栈:C/C++、Linux系统编程、计算机网络、数据结构、Mysql、Python 🍇 博客介绍:通过分享学习过程,加深知识点的掌握,也希望通过平台能

    2024年03月23日
    浏览(45)
  • boost 搜索引擎

    done 公司:百度、搜狗、360搜索、头条新闻客户端 - 我们自己实现是不可能的! 站内搜索:搜索的数据更垂直,数据量其实更小 boost的官网是没有站内搜索的,需要我们自己做一个 首先在用户进行搜索之前,在公司的服务器server上,内存上有一个searcher服务,而我们想进行搜

    2024年02月11日
    浏览(51)
  • boost库搜索引擎

    Gitee仓库:boost库搜索引擎 市面上有很多搜索引擎例如Google、百度、360等,这些都是特别大的项目。 对于个人学习我们可以写一个 站内搜索 ,这个搜索的内容更加垂直,数据量更小,例如C++的文档The C++ Resources Network Google搜索显示内容: 客户端使用浏览器搜索向服务器发起

    2024年04月09日
    浏览(51)
  • Boost搜索引擎

    先说一下什么是搜索引擎,很简单,就是我们平常使用的百度,我们把自己想要所有的内容输入进去,百度给我们返回相关的内容.百度一般给我们返回哪些内容呢?这里很简单,我们先来看一下. 这里我们简单的说一下我们的搜索引擎的基本原理. 我们给服务器发起请求,例如搜索关键

    2024年01月19日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包