BoostCompass( 查找功能实现 )

在这里插入图片描述

阅读导航

  • 一、查找功能基本思路
  • 二、详细代码
  • 三、代码介绍
  • 四、运行结果

一、查找功能基本思路

通过实现一个基于倒排索引的搜索引擎,来提供高效、准确的搜索服务。其核心在于快速准确地从大量文档中检索出与用户查询关键词相关的文档,并按照相关性对结果进行排序。其中分为五个步骤:倒排索引的构建、分词处理、搜索查询处理、结果排序、结果格式化输出

  1. 倒排索引的构建:搜索引擎首先需要构建一个倒排索引,这是一种将文档中出现的每个单词与包含该单词的文档列表相关联的数据结构。这样,当用户发起搜索请求时,搜索引擎可以迅速找到包含特定关键词的所有文档。

  2. 分词处理:由于搜索引擎通常需要处理自然语言文本,因此对用户输入的查询字符串进行分词是必要的步骤。这样可以帮助搜索引擎更准确地匹配文档中的关键词。

  3. 搜索查询处理:用户发起的搜索请求会被处理成一系列的关键词查询。搜索引擎会利用倒排索引来查找每个关键词相关的文档,并根据一定的算法(如权重累加)合并这些结果。

  4. 结果排序:为了提供最相关的搜索结果,搜索引擎会对查询结果进行排序。通常,这会根据文档与查询关键词的相关性(如关键词出现的频率、位置等因素)来确定排序。

  5. 结果格式化输出:最后,搜索引擎会将排序后的搜索结果格式化为易于用户理解的形式,如JSON格式,这样用户或其他应用程序就可以方便地读取和使用这些结果。

通过这种方式,搜索引擎能够快速响应用户的查询请求,提供相关性强、准确度高的搜索结果,从而提升用户体验。

二、详细代码

⭕searcher.hpp

#pragma once

// 引入所需的头文件
#include "index.hpp"  // 索引库
#include "util.hpp"   // 工具库
#include "log.hpp"    // 日志库
#include <algorithm>  // C++ 标准库算法
#include <unordered_map>       // 哈希表容器
#include <jsoncpp/json/json.h> // JSON处理库

// 定义命名空间ns_searcher
namespace ns_searcher {
    // 定义用于打印的倒排元素结构体
    struct InvertedElemPrint {
        uint64_t doc_id; // 文档ID
        int weight; // 权重
        std::vector<std::string> words; // 包含的关键字
        InvertedElemPrint() : doc_id(0), weight(0) {} // 默认构造函数
    };

    // 定义搜索引擎类Searcher
    class Searcher {
    private:
        // 索引对象指针
        ns_index::Index *index;

    public:
        // 构造函数
        Searcher() {}
        // 析构函数
        ~Searcher() {}

        // 初始化搜索引擎
        void InitSearcher(const std::string &input) {
            // 获取或创建索引对象
            index = ns_index::Index::GetInstance();
            LOG(NORMAL, "获取index单例成功...");
            // 根据索引对象建立索引
            index->BuildIndex(input);
            LOG(NORMAL, "建立正排和倒排索引成功...");
        }

        // 执行搜索查询,query: 搜索关键字json_string: 返回给用户浏览器的搜索结果
        void Search(const std::string &query, std::string *json_string) {
            // 对查询关键字进行分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query, &words);

            // 准备用于存储搜索结果的结构
            std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;
            std::vector<InvertedElemPrint> inverted_list_all;

            // 遍历分词结果,查询索引并合并结果
            for (std::string word : words) {
                boost::to_lower(word); // 转换为小写
                ns_index::InvertedList *inverted_list = index->GetInvertedList(word);
                if (inverted_list) {
                    for (const auto &elem : *inverted_list) {
                        auto &item = tokens_map[elem.doc_id]; // 获取或创建对应doc_id的InvertedElemPrint
                        item.doc_id = elem.doc_id;
                        item.weight += elem.weight;
                        item.words.push_back(elem.word);
                    }
                }
            }

            // 将合并后的结果添加到inverted_list_all
            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格式的搜索结果
            Json::Value root;
            for (auto &item : inverted_list_all) {
                ns_index::DocInfo *doc = index->GetForwardIndex(item.doc_id);
                if (doc) {
                    Json::Value elem;
                    elem["title"] = doc->title;// 文档标题
                    elem["desc"] = GetDesc(doc->content, item.words[0]); // 获取描述
                    elem["url"] = doc->url; // 文档URL
                    elem["id"] = (int)item.doc_id; // 文档ID
                    elem["weight"] = item.weight; // 权重

                    root.append(elem);
                }
            }

            // 将JSON对象转换为字符串
            Json::FastWriter writer;
            *json_string = writer.write(root);
        }

        // 获取文档描述的辅助函数
        std::string GetDesc(const std::string &html_content, const std::string &word) {
            // 查找word在html_content中的首次出现位置
            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);

            // 确定描述的起始和结束位置
            int start = pos - 50;
            int end = pos + 100;
            if (start < 0) start = 0;
            if (end > (int)html_content.size()) end = (int)html_content.size();

            // 截取描述内容并返回
            std::string desc = html_content.substr(start, end - start);
            desc += "...";
            return desc;
        }
    };
}

三、代码介绍

  1. 倒排元素结构体:在ns_searcher命名空间内部,定义了一个结构体InvertedElemPrint,用于存储文档ID、权重和包含的关键字。这个结构体用于打印和展示搜索结果中的倒排列表元素。

  2. 搜索引擎类:定义了一个名为Searcher的类,它是搜索引擎的核心。这个类包含一个指向索引对象的指针,该索引对象用于存储和管理文档数据。

  3. 构造函数和析构函数Searcher类有默认的构造函数和析构函数,用于初始化和销毁对象。

  4. 初始化搜索引擎InitSearcher方法用于获取或创建索引对象,并根据该索引对象建立正排和倒排索引。这是搜索引擎工作的前提。

  5. 执行搜索查询Search方法是搜索引擎的核心功能,它接收一个查询字符串,对该字符串进行分词处理,然后查询索引并合并搜索结果。搜索结果根据权重进行排序,并构建成JSON格式返回给用户。

  6. 分词处理:使用ns_util::JiebaUtil::CutString方法对查询字符串进行分词,这是中文文本处理中常见的步骤,以便更好地匹配文档中的关键词。

  7. 倒排索引查询:对于分词后的每个关键词,通过index->GetInvertedList方法查询倒排列表,并将结果合并到一个哈希表中。

  8. 结果排序和构建:合并后的搜索结果被添加到一个向量中,并根据权重进行降序排序。之后,构建JSON格式的搜索结果,包括文档标题、描述、URL和文档ID等信息。

  9. 获取文档描述的辅助函数GetDesc方法用于从HTML内容中提取包含特定关键词的描述性文本片段。它查找关键词在内容中的首次出现位置,并截取前后各50个字符作为描述内容。

四、运行结果

🥰如下图所示在搜索引擎的查询结果中,我们成功地提取了关键信息,包括文档的标题、摘要和URL。

在这里插入图片描述

✅这些信息为用户提供了一个直观的概览,使得用户能够快速识别和访问他们感兴趣的具体内容。标题作为文档的主题概述,摘要提供了内容的简短描述,而URL则直接指向了原始资源的网络地址。通过这种方式,用户可以高效地从搜索结果中筛选和导航到他们寻求的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/551286.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算机考研】「软件工程」VS「电子信息」专硕有什么不同?

就今年的24国考来说&#xff0c;计算机技术&#xff08;085404&#xff09;能报的只是比计算机科学与技术少那么一点点&#xff08;因为“计算机类”它都可以报&#xff0c;只有写计算机科学与技术的报不了&#xff09;相对于其他天坑专业来说还是好很多的&#xff01; 本人双…

制造企业研发设计资源用共享云桌面集中管控有哪些优势?

在制造企业上云的过程中&#xff0c;因为它们多用3D设计软件&#xff0c;所以选择一款高效、稳定、安全的云桌面产品显得尤为重要。云飞云共享云桌面作为一种新型的云桌面产品&#xff0c;正逐渐受到越来越多制造企业的青睐。那么&#xff0c;制造企业为什么要选云飞云共享云桌…

PaddleOCR训练自己模型(2)----参数配置及训练

一、介绍 paddleocr分为文字定位(Det)和文字识别(Rec)两个部分 二、定位模型训练 &#xff08;1&#xff09;Det预训练模型下载&#xff1a;https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_det_train.tar &#xff08;2&#xff09;下载完之后&#xff0c;…

(十一)C++自制植物大战僵尸游戏客户端更新实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/cFP3z 更新检查 游戏启动后会下载服务器中的版本号然后与本地版本号进行对比&#xff0c;如果本地版本号小于服务器版本号就会弹出更新提示。让用户选择是否更新客户端。 在弹出的更新对话框中有显示最新版本更新的内容…

React-hooks:useRef

useRef文档 useRef 是一个ReactHook&#xff0c;它能帮助引用一个不需要渲染的值。 const ref useRef(initialValue)参数 initialValue&#xff1a;ref对象的 current 属性的初始值&#xff0c;可以是任意类型的值&#xff0c;这个参数在首次渲染后被忽略。 返回值 useRe…

Day99:云上攻防-云原生篇K8s安全实战场景攻击Pod污点Taint横向移动容器逃逸

目录 云原生-K8s安全-横向移动-污点Taint 云原生-K8s安全-Kubernetes实战场景 知识点&#xff1a; 1、云原生-K8s安全-横向移动-污点Taint 2、云原生-K8s安全-Kubernetes实战场景 云原生-K8s安全-横向移动-污点Taint 如何判断实战中能否利用污点Taint&#xff1f; 设置污点…

Java中的装箱和拆箱

本文先讲述装箱和拆箱最基本的东西&#xff0c;再来看一下面试笔试中经常遇到的与装箱、拆箱相关的问题。 目录&#xff1a; 装箱和拆箱概念 装箱和拆箱是如何实现的 面试中相关的问题 装箱和拆箱概念 Java为每种基本数据类型都提供了对应的包装器类型&#xff0c;至于为…

Xshell无法输入命令输入命令卡顿

Xshell是一款功能强大的终端模拟软件&#xff0c;可以让用户通过SSH、Telnet、Rlogin、SFTP等协议远程连接到Linux、Unix、Windows等服务器。然而&#xff0c;在使用Xshell的过程中&#xff0c;我们可能会遇到一些问题。比如输入不了命令&#xff0c;或者输入命令很卡。这些问题…

React-Redux(二)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-Redux&#xff08;二&#xff09; 目录 react-redux 模块化 redux-thunk react-redu…

【pyhon】while语句的题目

1.计算1至100的偶数之和 sum_even 0 # 初始化偶数之和为0 i 1 # 从1开始循环 while i < 100: # 当i小于或等于100时&#xff0c;继续循环 if i % 2 0: # 如果i是偶数 sum_even i # 将i加到偶数之和上 i 1 # i自增1 print(“1至100的偶数之和为:”, sum_even) 给出乘…

SpringBoot源码解析-02

5. 模板引擎 由于 SpringBoot 使用了嵌入式 Servlet 容器 (tomca)。所以 JSP 默认是不能使用的。如果需要服务端页面渲染&#xff0c;优先考虑使用 模板引擎。 模板引擎页面默认放在 src/main/resources/templates SpringBoot 包含以下模板引擎的自动配置 FreeMarkerGroov…

Java NIO,高效操作I/O流的必备技能

Java IO在工作中其实不常用到&#xff0c;更别提NIO了。但NIO却是高效操作I/O流的必备技能&#xff0c;如顶级开源项目Kafka、Netty、RocketMQ等都采用了NIO技术&#xff0c;NIO也是大多数面试官必考的体系知识。虽然骨头有点难啃&#xff0c;但还是要慢慢消耗知识、学以致用哈…

设计模式:观察者模式(Observer)

设计模式&#xff1a;观察者模式&#xff08;Observer&#xff09; 设计模式&#xff1a;观察者模式&#xff08;Observer&#xff09;模式动机模式定义模式结构时序图模式实现观察者模式在单线程环境下的测试观察者模式在多线程环境下的测试多线程下的观察者模式模式分析优缺点…

计算机不联网是否有IP地址

计算机不联网是否会有IP地址&#xff0c;这个问题涉及到计算机网络的基础知识。要深入探讨这个问题&#xff0c;我们需要从IP地址的定义、作用&#xff0c;以及计算机在不联网状态下的网络配置等多个方面进行分析。 首先&#xff0c;IP地址&#xff08;Internet Protocol Addre…

HCIA--综合实验(超详细)

要求&#xff1a; 1. 使用172.16.0.0/16划分网络 2.使用ospf协议合理规划区域保证更新安全 3.加快收敛速度 4. r1为DR没有BDR 5.PC2&#xff0c;3&#xff0c;4&#xff0c;5自动获取IP地址&#xff1b;PC1为外网&#xff0c;PC要求可用互相访问 6.r7为运营商&#xff0c;只能配…

Oracle 正则,开窗,行列转换

1.开窗函数 基本上在查询结果上添加窗口列 1.1 聚合函数开窗 基本格式: ..... 函数() over([partition by 分组列,...][order by 排序列 desc|asc][定位框架]) 1&#xff0c;partition by 字段 相当于group by 字段 起到分组作用2&#xff0c;order by 字段 即根据某个字段…

Java实现优先级队列(堆)

前言 在学习完二叉树的相关知识后&#xff0c;我们对数据结构有了更多的认识&#xff0c;本文将介绍到优先级队列(堆&#xff09; 1.优先级队列 1.1概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能…

万兆以太网MAC设计(1)10G PCS PMA IP核使用

文章目录 一、设计框图二、模块设计三、IP核配置四、上板验证五、总结 一、设计框图 关于GT高速接口的设计一贯作风&#xff0c;万兆以太网同样如此&#xff0c;只不过这里将复位逻辑和时钟逻辑放到了同一个文件ten_gig_eth_pcs_pma_0_shared_clock_and_reset当中。如果是从第…

将图片按灰度转换成字符

from struct import *ch [., :, !, ~, ,, ^, *,$, #] ch.reverse()def calc(R, G, B):#模式Lreturn R * 299 // 1000 G * 587 // 1000 B * 144 / 1000def character( val):num val / 260 * len(ch)num round(num)if num>len(ch):numlen(ch)-1return ch[num]class rmb:d…

浅谈Spring的Bean生命周期

在Spring框架中&#xff0c;Bean&#xff08;即Java对象&#xff09;的生命周期涵盖了从创建到销毁的全过程&#xff0c;主要包含以下几个阶段&#xff1a; 实例化&#xff08;Instantiation&#xff09;&#xff1a; 当Spring IoC容器需要创建一个Bean时&#xff0c;首先会通过…