跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 搜索引擎核心模块:Searcher 设计与代码解析

C++ 搜索引擎核心模块 Searcher 基于单例模式管理正倒排索引,负责处理用户查询并返回结果。流程涵盖分词、触发检索、合并排序及 JSON 序列化。利用哈希表对文档 ID 去重,累加权重以提升相关性,并通过摘要函数提取关键词上下文。最终输出结构化数据供前端展示,实现从关键词输入到清晰搜索结果的高效匹配。

CloudNative发布于 2026/3/28更新于 2026/6/1224 浏览
C++ 搜索引擎核心模块:Searcher 设计与代码解析

Searcher 类作为上层封装,负责调用底层索引文件并实现搜索功能。其核心职责是处理用户搜索词,并根据处理结果返回对应的网页信息。

1. 单例模式

这里采用单例模式实例化 Searcher,同时建立正倒排索引。

private: ns_index::Index* index;
public: Searcher(){}
~Searcher(){}
public: void InitSearcher(const std::string& input) {
    // 1 创建(获取)一个 index 对象
    // 在这里我们用的是单例模式
    index=ns_index::Index::Getinstance();
    // 2 根据对象建立索引
    index->BuildIndex(input);
    LOG1(NORMAL,"建立索引成功...");
}

2. Search 流程

该函数的主要流程包括分词、触发检索、合并排序和构建 JSON 结果四个步骤。

2.1 分词

先创建一个 words 数组,使用 CutString 函数把用户提供的关键字分词并交给 words。

2.2 触发

获取单例模式中的倒排索引,通过 index->GetInvertedList(w) 获取关键词 w 对应的倒排索引列表,然后存入 tokens_map。由于 tokens_map 是哈希结构,会自动实现去重。to_lower 用于实现小写化,避免区分大小写导致匹配失败。

在遍历倒排列表时,元素引用直接操作 tokens_map,确保效率。

2.3 合并

将处理好的倒排列表统一存入 inverted_list_all。之所以使用 vector 是因为访问更方便,接着根据自身的权重进行从大到小排序。

2.4 构建 JSON

这一步即序列化,将内容转变为标准的、线性的、可存储或可传输的格式。代码中通过 Json 库实现序列化,将单个序列化的结果交给总的 root。

Json::StyledWriter writer;
// ... root.append(elem);
*json_string=writer.write(root); // 完成序列化

注意:在这份代码里一会获取正排索引,一会获取倒排索引,但它们都与 inverted_list_all 有关。inverted_list_all 的本质是对多个倒排列表进行'去重、合并、排序'后的候选文档集合,是连接'索引查询'和'结果返回'的中间数据结构。

void Search(const std::string& query,std::string* json_string) { // 1. 分词 std::vector<std::string>words; ns_util::JiebaUtil::CutString(query,&words); // 2. 触发 std::vector<InvertedElemPrint> inverted_list_all; std::unordered_map<uint64_t,InvertedElemPrint> tokens_map; // 哈希去重 for(std::string w:words) { boost::to_lower(w); // 忽略大小写 ns_index::InvertedList* inverted_list=index->GetInvertedList(w); if(inverted_list==nullptr) continue; for(const auto &elem : *inverted_list){ auto &item = tokens_map[elem.doc_id]; // 引用避免重复查找 item.doc_id = elem.doc_id; item.weight += elem.weight; item.words.push_back(elem.word); } } // 3. 合并 for(const auto &item : tokens_map) inverted_list_all.push_back(std::move(item.second)); std::sort(inverted_list_all.begin(), inverted_list_all.end(), [](const InvertedElemPrint &e1, const InvertedElemPrint &e2){ return e1.weight > e1.weight; }); // 4. 构建 Json::Value root; for(auto& item:inverted_list_all) { ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id); if(doc==nullptr) continue; Json::Value elem; elem["title"]=doc->title; elem["desc"]=GetDesc(doc->content,item.words[0]); elem["url"]=doc->url; root.append(elem); } Json::StyledWriter writer; *json_string=writer.write(root); }

3. GetDesc 摘要生成

该函数用于寻找摘要,即在 html_content 里面查找是否出现了关键字 word。相当于浏览器帮我们选出和这个词相关的网页信息的过程。

条件判断确认网页信息的长度是否足够,如果不够则直接截取全部,防止越界访问。

std::string GetDesc(const std::string& html_content,const std::string& word) {
    int prev_step=50;
    int next_step=100;
    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";
    int pos=std::distance(html_content.begin(),iter);
    if(pos==std::string::npos) return "None1";
    int start=0;
    int end=html_content.size()-1;
    if(pos-prev_step>start) start=pos-prev_step;
    if(pos+next_step<end) end=pos+next_step;
    if(start>=end) return "None2";
    std::string desc=html_content.substr(start,end-start);
    desc+="...";
    return desc;
}

4. InvertedElemPrint 结构体

这个结构体的出现是为了解决重复文档的问题。作为'去重与信息聚合'的载体,解决'同一文档因匹配多个关键词而重复出现'的问题。

通过 doc_id 作为核心依据,配合哈希表实现'同一文档只被记录一次'。当用户查询包含多个关键词时,同一文档可能匹配多个关键词并出现在多个倒排列表中,InvertedElemPrint 借助哈希表的键唯一性,避免文档在结果中重复出现。

struct InvertedElemPrint{
    uint64_t doc_id;
    int weight;
    std::vector<std::string> words;
    InvertedElemPrint(): doc_id(0), weight(0) {}
};

5. 完整代码实现

通过'索引构建'提前做好数据准备,通过'查询处理'高效匹配并排序文档,通过'结果格式化'衔接前端展示,最终实现'用户输入关键词→得到清晰搜索结果'的完整功能。

#pragma once
#include"index.hpp"
#include"usuallytool.hpp"
#include<algorithm>
#include<jsoncpp/json/json.h>
#include"log.hpp"
namespace ns_searcher{
struct InvertedElemPrint{
    uint64_t doc_id;
    int weight;
    std::vector<std::string> words;
    InvertedElemPrint(): doc_id(0), weight(0) {}
};
class Searcher{
private:
    ns_index::Index* index;
public:
    Searcher(){};
    ~Searcher(){}
public:
    void InitSearcher(const std::string& input) {
        index=ns_index::Index::Getinstance();
        index->BuildIndex(input);
        LOG1(NORMAL,"建立索引成功...");
    }
    void Search(const std::string& query,std::string* json_string) {
        std::vector<std::string>words;
        ns_util::JiebaUtil::CutString(query,&words);
        std::vector<InvertedElemPrint> inverted_list_all;
        std::unordered_map<uint64_t,InvertedElemPrint> tokens_map;
        for(std::string w:words) {
            boost::to_lower(w);
            ns_index::InvertedList* inverted_list=index->GetInvertedList(w);
            if(inverted_list==nullptr) continue;
            for(const auto &elem : *inverted_list){
                auto &item = tokens_map[elem.doc_id];
                item.doc_id = elem.doc_id;
                item.weight += elem.weight;
                item.words.push_back(elem.word);
            }
        }
        for(const auto &item : tokens_map)
            inverted_list_all.push_back(std::move(item.second));
        std::sort(inverted_list_all.begin(), inverted_list_all.end(), [](const InvertedElemPrint &e1, const InvertedElemPrint &e2){
            return e1.weight > e1.weight;
        });
        Json::Value root;
        for(auto& item:inverted_list_all) {
            ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id);
            if(doc==nullptr) continue;
            Json::Value elem;
            elem["title"]=doc->title;
            elem["desc"]=GetDesc(doc->content,item.words[0]);
            elem["url"]=doc->url;
            root.append(elem);
        }
        Json::StyledWriter writer;
        *json_string=writer.write(root);
    }
    std::string GetDesc(const std::string& html_content,const std::string& word) {
        int prev_step=50;
        int next_step=100;
        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";
        int pos=std::distance(html_content.begin(),iter);
        if(pos==std::string::npos) return "None1";
        int start=0;
        int end=html_content.size()-1;
        if(pos-prev_step>start) start=pos-prev_step;
        if(pos+next_step<end) end=pos+next_step;
        if(start>=end) return "None2";
        std::string desc=html_content.substr(start,end-start);
        desc+="...";
        return desc;
    }
};
}

目录

  1. 1. 单例模式
  2. 2. Search 流程
  3. 2.1 分词
  4. 2.2 触发
  5. 2.3 合并
  6. 2.4 构建 JSON
  7. 3. GetDesc 摘要生成
  8. 4. InvertedElemPrint 结构体
  9. 5. 完整代码实现
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • MySQL 权限管理与 C/C++ 开发对接指南
  • AI 辅助贪吃蛇游戏开发全流程解析
  • 大模型微调的技术含量:从数据构建到实验分析的深度解析
  • Java 手写 AI Agent:ZenoAgent 实战笔记
  • Python 字典与结构化数据核心用法
  • 2024 年 RAG 技术重大突破:全年革新与里程碑综述
  • PyWebIO 低代码开发指南:5 个实战案例构建 Python Web 应用
  • Django Web 框架实战:从零构建产品管理系统
  • GitHub 上令人惊艳的开源项目推荐
  • PyQt5 基础与常用控件入门教程
  • Web 项目 UI 自动化测试实战:从零搭建博客系统测试框架
  • Go 语言常见面试题及参考答案
  • Clawdbot(Moltbot) 飞书机器人配置教程
  • MySQL 数据类型详解:选型策略与常见误区
  • OpenClaw 开源 AI 智能体框架的诞生背景与历史回顾
  • Photoshop 中集成 Stable Diffusion 的 5 个核心技巧
  • Python 零基础转行学习路线与核心技能详解
  • 工科男生自学 Python:从办公自动化到数据分析的实战路径
  • 大模型面试指南:基础、微调、LangChain 及推理面经
  • Claude Code 命令行工具安装与环境配置指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online