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

C++ 搜索引擎 Searcher 模块源码解析:正倒排索引实现

C++ 搜索引擎 Searcher 模块源码解析正倒排索引实现。该模块负责处理用户搜索请求,通过单例模式管理索引,利用 Jieba 分词并结合倒排索引获取候选文档。核心流程涵盖触发查询、哈希去重合并权重、按权值排序及构建正向索引返回结果。摘要生成函数支持关键词定位与上下文截取,最终通过 JsonCpp 将搜索结果序列化为标准 JSON 格式返回给前端。涉及数据结构优化与内存管理细节。

涅槃凤凰发布于 2026/3/29更新于 2026/6/1018 浏览
C++ 搜索引擎 Searcher 模块源码解析:正倒排索引实现

Searcher 类作为上层封装,依赖底层索引文件实现具体业务逻辑。核心职责是处理用户搜索词,并根据匹配结果返回网页信息。

1. 单例模式

初始化阶段采用单例模式管理索引对象,确保全局唯一性并构建正倒排索引。

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 分词

首先创建字符串向量存储分词结果,使用 Jieba 工具将用户提供的关键字分词并交给 words 容器。

2.2 触发

从单例索引中获取倒排列表,通过 index->GetInvertedList(w) 获取关键词对应的倒排索引列表,存入哈希表 tokens_map。由于哈希结构特性,可自动实现去重。调用 boost::to_lower 统一转为小写,避免大小写区分导致的关键字失效。

遍历倒排列表时,元素引用直接操作哈希表中的节点,避免重复查找提高效率。

2.3 合并

将处理好的倒排数据统一存入 inverted_list_all,利用 vector 访问更方便的特性,接着根据权重进行从大到小排序。

注:代码中频繁提及正排与倒排索引,但核心都关联到 inverted_list_all。它是对多个倒排拉链进行'去重、合并、排序'后的候选文档集合,是连接'索引查询'和'结果返回'的中间数据结构。

2.4 构建 JSON

最后进行序列化,将内存中的 JSON 对象转换为标准传输格式。此处使用 JsonCpp 库的非标准库实现,无需手动编写序列化逻辑。将单个序列化的结果追加到根节点 root,最终通过 StyledWriter 写入可读性强的 JSON 字符串。

// query 为搜索关键字,json_string 是返回给用户的搜索结果
void Search(const std::string& query, std::string* json_string) {
    // 1. 分词
    std::vector<std::string> words;
    ns_util::JiebaUtil::(query, &words);

    
    std::vector<InvertedElemPrint> inverted_list_all; 
    std::unordered_map<, InvertedElemPrint> tokens_map; 

     (std::string w : words) {
        boost::(w); 
        ns_index::InvertedList* inverted_list = index->(w);
         (inverted_list == ) ;

         ( & elem : *inverted_list) {
            & item = tokens_map[elem.doc_id];
            
            
            item.doc_id = elem.doc_id;
            item.weight += elem.weight;
            item.words.(elem.word);
        }
    }

    
     ( & item : tokens_map)
        inverted_list_all.(std::(item.second));

    std::(inverted_list_all.(), inverted_list_all.(),
              []( InvertedElemPrint& e1,  InvertedElemPrint& e2) {
                   eweight > eweight;
              });

    
    Json::Value root;
     (& item : inverted_list_all) {
        ns_index::DocInfo* doc = index->(item.doc_id);
         (doc == ) ;
        Json::Value elem;
        elem[] = doc->title;
        elem[] = (doc->content, item.words[]);
        elem[] = doc->url;
        root.(elem);
    }

    Json::StyledWriter writer;
    *json_string = writer.(root);
}
CutString
// 2. 触发
// 用来存放去重后的关键字
uint64_t
// 哈希用于去重
for
to_lower
// 忽略大小写
GetInvertedList
if
nullptr
continue
for
const
auto
auto
// []:如果存在直接获取,如果不存在新建
// item 一定是 doc_id 相同的 print 节点
push_back
// 3. 合并
for
const
auto
push_back
move
sort
begin
end
const
const
return
1.
2.
// 4. 构建
for
auto
GetForwardIndex
if
nullptr
continue
"title"
"desc"
GetDesc
0
"url"
append
write

3. GetDesc

此函数负责生成摘要,即在 HTML 内容中查找是否出现关键词。若找到,截取关键词前后指定长度的内容;若未找到或长度不足,则返回空值。边界检查防止越界访问。

std::string GetDesc(const std::string& html_content, const std::string& word) {
    int prev_step = 50;
    int next_step = 100;
    
    // 在 html_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";
    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

该结构体用于解决文档重复问题。作为'去重与信息聚合'的载体,当同一文档匹配多个关键词时,借助哈希表的键唯一性,避免文档在结果中重复出现。

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 > e2.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. 完整源码参考
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • VS Code Python 代码格式化插件指南
  • Python 基于 Selenium 实现 12306 自动购票脚本
  • Flutter 与 OpenHarmony 下 web_socket 标准 WebSocket 客户端跨平台实现
  • Python 流程控制详解:条件与循环语句及实战
  • HDFS 分布式文件系统数据读写流程解析
  • C++ 多态详解:虚函数重写、虚表指针与动态绑定原理
  • 通义万相 2.1 文生图技术部署与特性解析
  • RAG 评估指南:核心指标解析与代码实现
  • 反射内存卡原理:PCIE-5565 架构与 FPGA 实现解析
  • 前端 EME DRM 反录屏原理与实战代码
  • Retinaface+CurricularFace 镜像内 Python 3.11.14 安全补丁升级方法
  • 简单的解压缩算法实现
  • 8 个 Python 高效数据分析技巧
  • 程序员与管理者进阶必读的 5 本经典好书
  • Retinaface+CurricularFace 镜像内 Python 3.11.14 安全补丁升级方法
  • 五年程序员兼职接单经验总结与避坑指南
  • Python PyQt6 桌面应用开发实战指南
  • 分布式任务调度:多数据库兼容的策略封装
  • Java 注解与反射实战:实现自定义日志与参数校验注解
  • Spring Boot 集成本地 OCR 服务模块实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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