7.编写倒排索引|编写index单例|编写查找代码

建立倒排索引的时候要忽略大小写

bool BuildInvertedIndex(const DocInfo &doc)
{
	//DocInfo{title, content, url, doc_id}
	//word -> 倒排拉链
	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; //用来暂存词频的映射表


	//对标题进行分词
	std::vector<std::string> title_words;
	ns_util::JiebaUtil::CutString(doc.title, &title_words);

	//对标题进行词频统计
	for(std::string s : title_words)
	{
		boost::to_lower(s); //统一转化成为小写
		word_map[s].title_cnt++; //如果存在就获取,如果不存在就新建     
	}

	//对内容进行分词
	std::vector<std::string> content_words;
	ns_util::JiebaUtil::CutString(doc.content, &content_words);

	//对内容进行词频统计
	for(std::string s : content_words){
		boost::to_lower(s);
		word_map[s].content_cnt++;
	}

#define X 10
#define Y 1

	for(auto &word_pair : word_map){
		InvertedElem item;
		item.doc_id = doc.doc_id;
		item.word = word_pair.first;
		item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt; //相关性
		InvertedList &inverted_list = inverted_index[word_pair.first];
		inverted_list.push_back(std::move(item));
	}

	return true;
}

编写搜索引擎模块Searcher

基本代码结构

搜索的关键字也要在服务端进行分词,然后,才能进行查找index
新建searcher.hpp

touch searcher.hpp

![[Pasted image 20250216115016.png]]

#include "index.hpp"

namespace ns_searcher{
	class Searcher{
		private:
			ns_index::Index *index; //供系统进行查找的索引
		public:
			Searcher(){}
			~Searcher(){}
		public:
			void InitSearcher(const std::string &input)
			{
				//1.获取或者创建index对象
				//2.根据index对象建立索引
			}
			//query:搜索关键字
			//json_string:返回给用户浏览器的搜索结果
			void Search(const std::string &query, std::string *json_string)
			{
				//1.[分词]:对query进行按照searcher的要求进行分词
				//2.[触发]:就是根据分词的各个词,进行对应的index搜索
				//3.[合并排序]:根据汇总查找结果,按照相关性(weight)降序排序
				//4.[构建]:根据对应的查找出来的结果,构建json串 -- jsoncpp
			}
	};
}    
编写index单例

index.hpp

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include "util.hpp"

namespace ns_index{

	struct DocInfo{
		std::string title;   //文档标题
		std::string content; //文档对应的去标签之后的内容
		std::string url;     //官网文档url
		uint64_t dic_id;     //文档的ID
	}

	struct InvertedElem{
		uint64_t doc_id;
		std::string word;
		int weight;
	}

	//倒排拉链
	typedef std::vector<InvertedElem> InvertedList;

	class Index{
		private:
			//正排索引的数据结构用数组,数组的下标天然是文档的ID
			std::vector<DocInfo> forward_index; //正排索引
			//倒排索引一定是一个关键字和一组(个)InvertedElem对应[关键字和倒排拉链的映射关系]
			std::unordered_map<std::string, InvertedList> inverted_index;
		private:
			Index(){} //但是一定要有函数体,不能delete
			Index(const Index&) = delete;
			Index& operator = (const Index&) = delete;

			static Index* instance;
			static std::mutex mtx;
		public:
			~Index(){}
		public:
			static Index* GetInstance()
			{
				if(nullptr == instance){
					mtx.lock();
					if(nullptr == instance){
							instance = new Index();
					}
					mtx.unlock();
					return instance;
				}
			}
			//根据doc_id找到文档内容
			DocInfo *GetForwardIdex(uint64_t doc_id)
			{
				if(doc_id >= forward_index.size()){
					std::cerr << "doc_id out range, error" << std::endl;
					return nullptr;
				}
				return &forward_index[doc_id];
			}

			//根据关键字string获得倒排拉链
			InvertedList *GetInvertedList(const std::string &word)
			{
				auto iter = inverted_index.find(word);
				if(iter == inverted_index.end()){
					std::cerr << word << " have no InvertedList" << std::endl;
					return nullptr;
				}

				return &(iter->second);
			}
			//根据去标签,格式化之后的文档,构建正排和倒排索引
			//data/raw_html/raw.txt
			bool BuildIndex(const std::string &input) //parse处理完毕的数据交给我
			{
				std::ifstream in(input, std::ios::in | std::ios::binary);
				if(!in.is_open()){
					std::cerr << "sorry, " << input << " open error" << std::endl;
					return false;
				}

				std::string line;
				while(std::getline(in, line)){
					DocInfo * doc = BuildForwardIndex(line);
					if(nullptr == doc){
						std::cerr << "build " << line << " error" << std::endl; //for debug
						continue;
					}

					BuildInvertedIndex(*doc);
				}
				return true;
			}
		private:
			DocInfo *BuildForwardIndex(const std::string &line)
			{
				//1.解析line,字符串切分
				//line -> 3 string, title, content, url
				std::vector<std::string> results;
				const std::string sep = '\3';  //行内分隔符
				ns_util::StringUtil::CutString(line, &results, sep);
				if(results.size() != 3){
					return nullptr;
				}
				//2.字符串进行填充到DocInfo中
				DocInfo doc;
				doc.title = results[0]; //title
				doc.content = results[1]; //content
				doc.url = results[2]; //url
				doc.doc_id = forward_index.size(); //先保存id,再插入,对应的id就是当前doc在vector中的下标
				//3.插入到正排索引的vector中
				forward_index.push_back(std::move(doc)); //doc.html文件内容
				return &forward_index.back();
			}

			bool BuildInvertedIndex(const DocInfo &doc)
			{
				//DocInfo{title, content, url, doc_id}
				//word -> 倒排拉链
				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; //用来暂存词频的映射表

				//对标题进行分词
				std::vector<std::string> title_words;
				ns_util::JiebaUtil::CutString(doc.title, &title_words);

				//对标题进行词频统计
				for(std::string s : title_words)
				{
					boost::to_lower(s); //统一转化成为小写
					word_map[s].title_cnt++; //如果存在就获取,如果不存在就新建     
				}

				//对内容进行分词
				std::vector<std::string> content_words;
				ns_util::JiebaUtil::CutString(doc.content, &content_words);

				//对内容进行词频统计
				for(std::string s : content_words){
					boost::to_lower(s);
					word_map[s].content_cnt++;
				}

#define X 10
#define Y 1

				for(auto &word_pair : word_map){
					InvertedElem item;
					item.doc_id = doc.doc_id;
					item.word = word_pair.first;
					item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt; //相关性
					InvertedList &inverted_list = inverted_index[word_pair.first];
					inverted_list.push_back(std::move(item));
				}

				return true;
			}
	};
	Index* Index::instance = nullptr;
}

searcher文件

#include "index.hpp"
  
namespace ns_searcher{
	class 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对象建立索引
				index->BuildIndex(input);
			}
			//query:搜索关键字
			//json_string:返回给用户浏览器的搜索结果
			void Search(const std::string &query, std::string *json_string)
			{
				//1.[分词]:对query进行按照searcher的要求进行分词
				//2.[触发]:就是根据分词的各个词,进行对应的index搜索
				//3.[合并排序]:根据汇总查找结果,按照相关性(weight)降序排序
				//4.[构建]:根据对应的查找出来的结果,构建json串 -- jsoncpp
			}

	};
}

编写查找代码

引入第三方库json

sudo yum install -y jsoncpp-devel

![[Pasted image 20250216172340.png]]

编写searcher文件

#include "index.hpp"
#include "util.hpp"
#include <algorithm>
#include <jsoncpp/json/json.h>

namespace ns_searcher{
	class 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对象建立索引
				index->BuildIndex(input);
			}
			//query:搜索关键字
			//json_string:返回给用户浏览器的搜索结果
			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查找,建立index是忽略大小写,所以搜索,关键字也需要
				ns_index::InvertedList inverted_list_all; //内部InvertedElem
				for(std::string s : words){
					boost::to_lower(word);

					ns_index::InvertedList *inverted_list = index->GetInvertedList(word);
					if(nullper == inverted_list){
							continue;
					}

					inverted_list_all.insert(inverted_list_all.end(), inverted_list.begin(), inverted_list.end());
				}
				//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;
							});
				//4.[构建]:根据对应的查找出来的结果,构建json串 -- jsoncpp完成序列化和反序列化
				Json::Value root;
				for(auto &item : inverted_list_all){
					ns_index::DocInfo * doc = index->GetForwardIndex(item.doc_id);
					if(nullptr == doc){
						continue;
					}
					Json::Value elem;
					elem["title"] = doc->title;
					elem["desc"] = doc->content; //content是文档的去标签的结果,但是不是我们要的,我们要一部分
					elem["url"] = doc->url;

					root.append(elem);
				}

				Json::StyledWriter writer;
				*json_string = writer.write(root);
			}

	};
}

建立server.cc

touch server.cc

![[Pasted image 20250216174723.png]]

#include "searcher.hpp"

int main()
{       
	return 0;
} 

可以进行搜索测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值