迷你搜索引擎

2023-11-17

这几天在思考如何对项目做出扩展,当一个网站访问量上升之后随之而来的便是用户的大量交流,根据现在主流的交流方式来看,一般都是一个用户先进行发帖,然后其他用户在下面对之评论,评论系统暂且搁置一边不谈,现在有一个问题就是当帖子数量越来越多,如何快速找到与关键词相对应的帖子,使用关键字在数据库中遍历不太现实,但其实仔细想想搜索引擎不就是做这个的吗,于是就想着实现一个简单的搜索引擎。

要实现一个简单的搜索引擎,必要的相关知识一定是必不可少的,简单来说,第一步先要对已有的资源进行整理,根据整理出来的结果构建索引,搜索模块根据给定关键词查找索引给出排序结果

对已有资源进行整理

搜索引擎使用的前提就是有大量的html文档,需要搜索引擎来做出某种处理来给出正确的结果,所以搜索引擎内部就需要对web杂乱的资源进行管理,以此来完成后面的工作,我们现在假设某一个html目录内有几千条html页面,我们需要对这些页面依次遍历进行统计,将页面中的标题,正文以及html对应在web中的url以某种格式保存下来,这里是保存在一个文件中。

C++提供的库中还不足以让我们拥有遍历整个目录这个功能,所以这里需要借助boost库。

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

const std::string g_input_path =  "../data/input"; // 待解析的html文档都在这个目录下
const std::string g_output_path = "../data/output/pase_file"; //将解析结果放在这个文件中

struct docInfo {
    std::string title;
    std::string url;
    std::string content;
};

bool enumFile(const std::string& input_path, std::vector<std::string>& file_list) {
    namespace fs = boost::filesystem;
    fs::path root_path(input_path);
    if(!fs::exists(root_path)) {
        std::cout << "input err " << __FILE__ << ":" << __LINE__ << std::endl;
        return false;
    }

    // boost递归遍历目录,借助迭代器
    fs::recursive_directory_iterator end_iter;    
    for(fs::recursive_directory_iterator iter(root_path); iter != end_iter; ++iter) {
        if(!fs::is_regular_file(*iter)) {
            continue; // 过滤掉目录
        }

        if(iter->path().extension() != ".html") {
            continue;
        }

        file_list.push_back(iter->path().string());
    }
    return true;
}

int main()
{
    std::vector<std::string> file_list; // 将所有出现过的页面路径都放在这个vector中

    if(enumFile(g_input_path, file_list)) {
        std::cout << "enmu file err " << __FILE__ << ":" << __LINE__ << std::endl;
        return 1;
    }

    std::ofstream out_file(g_output_path.c_str());
    if(!out_file.is_open()) {
        std::cout << "out file err " << __FILE__ << ":" << __LINE__ << std::endl;
        return -1;
    }

    // 依次处理每个html页面
    for(const auto& file_path : file_list) {
        struct docInfo info;
        if(parseFile(file_path, info)) {
            std::cout << "pase file err" << std::endl;
            continue;
        }
        writeOutPut(info, out_file);
    }
    out_file.close();
    return 0;
}

解析完毕之后在最终生成的配置文件中每一行就对应一个html文档

构建索引
  • 认识倒排索引

    • 在普通情况下的索引都是会根据文章的标题或者序号给出文章正文的内容,这种通常情况下的索引我们叫做正排索引
    • 倒排索引就是在给出关键词的情况下找出关键词所出现的文章标题或者序号。
  • 搜索引擎的工作流程

    • 先将用户输入词或句进行拆分(这里使用结巴分词
    • 根据拆分词结合权重在倒排索引中查找,根据返回id找到正排索引
    • 根据正排索引将搜索结果进行界面包装进行返回
  • 构建索引

struct docInfo {
	uint64_t doc_id;
	std::string title;
	std::string content;
	std::string url;
};

struct Weight {
	uint64_t doc_id;
	int weight; // 权重 为排序做准备
	std::string key;
};

std::vector<docInfo> forward_index; //正排索引容器
std::unordered_map<std::string, std::vector<Weight>> inverted_index; // 倒排容器

bool buildIndex(std::string& file_path) {
	// ... 判断文件相关
	
	// 读取配置文件 遍历每个解析过的文档
	std::string line;
	while(std::getline(file, line)) {
		// 构造正排索引 
		const docInfo *doc_info = buildForward(line);
		// 更新倒排索引
		buildInverted(*doc_info);
	}
}

const docInfo* buildForward(std::string html) {
	std::vector<std::string> tokens;
	// ...使用boost对读取的配置文件进行切分
	
	docInfo doc_info;
	doc_info.doc_id = forward_index.size();
	doc_info.title = tokens[0];
	doc_info.content = tokens[1];
	doc_info.url = tokens[2];
	
	forward_index.push_back(doc_info);
	return &forward_index.back();
}

void buildInverted(const docInfo& doc_info) {
	// 对doc_info中的标题和正文进行分词
	std::vector<std::string> title_tokens;
	cutWord(doc_info.title, title_tokens);
	
	std::vector<std::string> content_tokens;
	curWord(doc_info.content, content_tokens);
	
	// 对分词结果进行词频统计
	struct wordCount {
		int title_count;
		int content_count;
	};
	std::unordered_map<std::string, wordCount> word_count;
	
	for(std::string word : title_tokens) {
		boost::to_lower(word); // 全部转换成小写
		++word_count[word].title_count;
	}
	
	for(std::string word : content_tokens) {
		boost::to_lower(word);
		++word_count[word].content_count;
	}
	
	// 遍历结果 更新倒排索引
	for(const auto& word_pair : word_count) {
		Weight weight;
		weight.doc_id = doc_info.doc_id;
		weight.weight = 10 * word_pair.second.title_count + word_pair.second.content_count;
		weight.key = word_pair.first;
		
		auto& inverted_list = inverted_index[word_pair.first];
		inverted_index.push_back(weight);
	}
}
  • 索引已经正常构造完毕,剩下的就是将用户的输入拆分进行搜索
bool Search(const std::string& input_query, std::string& result) {
	// 分词 对查询词进行分词
	std::vector<std::string> tokens;
	curWord(input_query, tokens);
	
	// 触发	对分词结果进行查询
	std::vector<Weight> all_tokens_result;
	for(std::string word : tokens) {
		boost::to_lower(word);
		auto *inverted_list = index->getInvertedList(word); // 此处返回的是指向倒排链表的指针
		if(inverted_list == nullptr) {
			continue;
		}
		
		all_tokens_result.insert(all_tokens_result.end(),\
		inverted_list.begin(), inverted_list.end());
	}
	
	// 排序 对结果进行一定的排序
	std::sort(all_tokens_result.begin(), all_tokens_result.end(), 
				[](const Weight& w1, const Weight& w2){
					return w1.weight > w2.weight;
				});
				
	// 构造结果
	// ...
}

至此全部结构都已经完成, 点我获取源码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

迷你搜索引擎 的相关文章

  • C++ 是否可以在 MacOS 上与 OpenMP 和 boost 兼容?

    我现在已经尝试了很多事情并得出了一些结论 也许 我监督了一些事情 但似乎我无法完成我想要的事情 问题是 是否有可能使用 OpenMP 和 boost 在 MacOS High Sierra 上编译 C 一些发现 如果我错了请纠正我 Open
  • IdentityServer 4 对它的工作原理感到困惑

    我阅读和观看了很多有关 Identity Server 4 的内容 但我仍然对它有点困惑 因为似乎有很多移动部件 我现在明白这是一个单独的项目 它处理用户身份验证 我仍然不明白的是用户如何注册它 谁存储用户名 密码 我打算进行此设置 Rea
  • 从同一个类中的另一个构造函数调用构造函数

    我有一个带有两个构造函数的类 C 这是代码片段 public class FooBar public FooBar string s constructor 1 some functionality public FooBar int i
  • 查看 NuGet 包依赖关系层次结构

    有没有一种方法 文本或图形 来查看 NuGet 包之间的依赖关系层次结构 如果您使用的是新的 csproj 您可以在此处获取所有依赖项 在项目构建后 项目目录 obj project assets json
  • C# 数据表更新多行

    我如何使用数据表进行多次更新 我找到了这个更新 1 行 http support microsoft com kb 307587 my code public void ExportCSV string SQLSyntax string L
  • 使用 GCP 的数据存储区时如何区分代码是在模拟器中运行还是在 GKE 中运行

    按照中给出的说明进行操作后 我不确定是否遗漏了任何内容https cloud google com datastore docs tools datastore emulator https cloud google com datasto
  • 对 std::vector 进行排序但忽略某个数字

    我有一个std vector
  • 在视口中查找 WPF 控件

    Updated 这可能是一个简单或复杂的问题 但在 wpf 中 我有一个列表框 我用一个填充数据模板从列表中 有没有办法找出特定的数据模板项位于视口中 即我已滚动到其位置并且可以查看 目前我连接到了 listbox ScrollChange
  • 在mysql连接字符串中添加应用程序名称/程序名称[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在寻找一种解决方案 在连接字符串中添加应用程序名称或程序名称 以便它在 MySQL Workbench 中的 客户端连接 下可见 SQL
  • C++ 中的双精度型数字

    尽管内部表示有 17 位 但 IEE754 64 位 浮点应该正确表示 15 位有效数字 有没有办法强制第 16 位和第 17 位为零 Ref http msdn microsoft com en us library system dou
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • 等待 IAsyncResult 函数直至完成

    我需要创建等待 IAsyncResult 方法完成的机制 我怎样才能做到这一点 IAsyncResult result contactGroupServices BeginDeleteContact contactToRemove Uri
  • 检测到严重错误 c0000374 - C++ dll 将已分配内存的指针返回到 C#

    我有一个 c dll 它为我的主 c 应用程序提供一些功能 在这里 我尝试读取一个文件 将其加载到内存 然后返回一些信息 例如加载数据的指针和内存块的计数到 c Dll 成功将文件读取到内存 但在返回主应用程序时 程序由于堆损坏而崩溃 检测
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • 实体框架中的“it”是什么

    如果以前有人问过这个问题 请原谅我 但我的任何搜索中都没有出现 它 我有两个数据库表 Person 和 Employee 对每个类型的表进行建模 例如 Employee is a Person 在我的 edmx 设计器中 我定义了一个实体
  • 在 Windows Phone silverlight 8.1 上接收 WNS 推送通知

    我有 Windows Phone 8 1 silverlight 应用程序 我想使用新框架 WNS 接收通知 我在 package appxmanifest 中有
  • 如何减少具有多个单元的 PdfPTable 的内存消耗

    我正在使用 ITextSharp 创建一个 PDF 它由单个 PdfTable 组成 不幸的是 对于特定的数据集 由于创建了大量 PdfPCell 我遇到了内存不足异常 我已经分析了内存使用情况 我有近百万个单元格的 1 2 在这种情况下有
  • Objective-C / C 给出枚举默认值

    我在某处读到过关于给枚举默认值的内容 如下所示 typedef enum MarketNavigationTypeNone 0 MarketNavigationTypeHeirachy 1 MarketNavigationTypeMarke
  • 不区分大小写的字符串比较 C++ [重复]

    这个问题在这里已经有答案了 我知道有一些方法可以进行忽略大小写的比较 其中涉及遍历字符串或一个good one https stackoverflow com questions 11635 case insensitive string
  • OpenCV SIFT 描述符关键点半径

    我正在深入研究OpenCV的SIFT描述符提取的实现 https github com Itseez opencv blob master modules nonfree src sift cpp 我发现了一些令人费解的代码来获取兴趣点邻域

随机推荐

  • WPF_性能优化

    WPF Windows Presentation Foundation 是微软推出的基于Windows的用户界面框架 运行在 NET Framework 3 0及以上版本 WPF是基于DirectX引擎的 支持GPU硬件加速 在不支持硬件加
  • 任务 01、重塑视觉艺术:Midjourney AI绘画的无限可能

    1 1 任务目标 了解什么是MidJourney MidJourney公司简介 了解生成式人工智能MidJourney原理 MidJourney 能做什么 目前市面主流的Ai绘画工具有哪些 MidJourney的商业价值与企业应用 1 2
  • js逆向-ast-hook定位参数生成位置

    声明 本文仅供参考学习 切勿用于其他途径 违者后果自负 前言 不了解ast hook的小伙伴可以翻看上一篇文章 链接 ast hook 以一个简单的网站为例 网址 aHR0cHM6Ly93d3cueGluaXVkYXRhLmNvbS8 接口
  • 算法与数据结构学习笔记

    文章目录 常用排序方式的时间 空间复杂度以及稳定性的总结 1 冒泡排序 2 选择排序 3 插入排序 4 希尔排序 基于插入排序 注意对比 5 归并排序 6 快速排序 最流行的排序算法 大多数情况都是最快的 7 堆排序 找出前几个前几个最大的
  • Ha-NeRF: Hallucinated Neural Radiance Fields in the Wild 代码复现与解读

    code GitHub rover xingyu Ha NeRF CVPR 2022 Ha NeRF Hallucinated Neural Radiance Fields in the Wild CVPR 2022 Ha NeRF Hal
  • 【批处理DOS-CMD-汇总】扩展变量-延迟变量cmd /v:on、cmd /v:off、setlocal enabledelayedexpansion、DisableDelayedExpansion

    Reference 批处理命令 for kaizen 博客园 Bat脚本之延时变量cmd v on komomon s blog的博客 CSDN博客 bat延迟变量 一 延迟变量 的存在背景 批处理的执行过程是 自上而下 逐条执行 而 逐条
  • Vue项目部署到服务器时上传报错“Uncaught (in promise) TypeError: s.upload.addEventListener is not a function”

    一 报错原因 使用vue admin element框架进行在本地文件上传以及富文本框中的文件上传是没有问题的 但是在上传部署vue项目到服务器上时 就会报如下图中一个错误 二 那么应该怎么解决呢 可以查找如下两个文件 并且进行对应值的修改
  • mysql查询时间datetime指定区间的所有值

    DROP TABLE IF EXISTS flight CREATE TABLE flight id int 11 NOT NULL start time datetime NOT NULL end time datetime NOT NU
  • python中有堆吗?

    堆 英语 heap 是计算机科学中一类特殊的数据结构的统称 堆的定义 n个元素的序列 k1 k2 ki kn 当且仅当满足下关系时 称之为堆 推荐学习 Python基础视频教程 这是标准的堆的定义 但是python 中并没有独立的堆类型 只
  • 微信小程序开发教程

    一 准备 下载微信小程序开发者工具 下载地址 注册微信小程序 前往注册 微信小程序开发文档 前往阅览 打开开发者工具 用微信扫码进入创建页面 填写配置如下 需要注意的是 AppId可以选择已经注册的账号Appid 也可以选择测试号 区别是测
  • 论文R语言复现

    高斯混合概率在众多领域都有重要应用 依据已知观测数据估计高斯模型中未知参数就显得尤为重要 由于观测值具体来自于高斯分布的哪个分模型是未知的 那么利用传统的极大似然 MLE 方法进行参数估计就变得十分困难 引入 EM 算法 该方法通过构造分布
  • [C++]模版特例化和模版偏特化

    函数模版特例化 例子 第一个版本 可以比较任意两个类型 template
  • 【事件驱动】【数码管识别】一(数码管检测(矩形检测函数解读))

    1 根据轮廓的三个点两条线的夹角 三点的位置关系 两个向量的夹角的余弦值等于两个向量的向量积除以两个向量的数量积 两个向量垂直则余弦值接近于0 该函数返回的就是余弦值 1e 10是1 10的负10次方 为了转换为doule型 2 找出矩形
  • C/C++之宏定义函数

    注意事项 1 将宏定义中的参数和整个宏 用 括起来 2 在宏定义结束的后面 不要加 宏定义只是简单的进行字符串替换 会把 也替换过去 include
  • Spring-1

    struts web层 比较简单 ValueStack值栈 拦截器 hibernate dao层 知识点杂 spring service层 重要 讲多少用多少 gt 了解 spring day01 基础 IoC控制反转 DI依赖注入 整合J
  • 安装完nodejs后在powershell使用node命令报错

    安装完nodejs后 在cmd中可以正常启动node 但是在powershell中出现如下错误 解决方法 鼠标右键点击计算机 打开属性 点击高级系统设置 然后打开环境变量 如下图所示 然后在下面的系统变量点击新建 添加如下图所示变量 然后编
  • 线上服务平均响应时间太长,怎么排查?

    最困难的事情就是认识自己 个人网站 欢迎访问 前言 最近线上环境某个接口服务响应时间偏长 导致用户体验超差 那平时该怎么快速的排查这类问题呢 为代码添加上详细的打印日志 不建议 一是线上环境 没法随便的重新部署更换了详细日志的代码 二是 添
  • 合并数组,升序排列

    public class Demo22 给两个数组 数组A 1 7 9 11 13 15 17 19 数组B 2 4 6 8 10 两个数组合并成数组C 按着升序排序 public static void main String args
  • k-Means——经典聚类算法实验(Matlab实现)

    聚类算法 k Means实验 k 平均 k Means 也被称为k 均值 是一种得到最广泛使用的聚类算法 1 k Means算法以k为参数 把n个对象分为k个簇 使得簇内具有较高的相似度 实验目的 了解常用聚类算法及其优缺点 掌握k Mea
  • 迷你搜索引擎

    这几天在思考如何对项目做出扩展 当一个网站访问量上升之后随之而来的便是用户的大量交流 根据现在主流的交流方式来看 一般都是一个用户先进行发帖 然后其他用户在下面对之评论 评论系统暂且搁置一边不谈 现在有一个问题就是当帖子数量越来越多 如何快