当前位置: 首页 > article >正文

文档搜索引擎

首先获取很多网页(爬虫->一个http客户端,发送http请求获取http响应结果(就是网站))(批量化的获取很多的页面)
再根据用户输入的查询词,在网页中进行查找

用户输入查询词之后,如何让查询词和当前这些网页进行匹配
->使用倒排索引

倒排索引
1.文档: 每个待搜索的网页(被爬虫技术,把各个网站的各个网页搜集在一起,然后保存到服务器上)
2. 正排索引: 文档Id(身份标识)-> 文档内容(包含很多段落,句子,词..)
3. 倒排索引: 词-> 文档id列表(包含词)

key和value不一样

项目目标: 实现一个针对Java文档的搜索引擎

百度是属于"全站搜索",搜索震哥哥互联网上的网站

B站是属于"站内搜索".只针对某个网站的内容进行搜索

把相关的文档获取到,才能制作正排索引和倒排索引

我们直接从官网上下载文档的压缩包即可

离线文档和在线文档的路径都是对应的, 我们可以根据离线文档的路径直接构造出在线文档的路径

模块划分

1> 索引模块

2> 搜索模块

3> web模块  

分词的概念

把一个完整的句子,切分为多个词

基于现成的第三方库来实现分词

分词的实现原理

1. 基于词库

2. 基于统计: 某俩个字在一起使用的概率很大,此刻我们就可以把它们标注为一个词

单身狗这种在词库里基本找不到,因为是网络造的词

实现分词的第三方库: ansj

<dependency>
<groupId>org.ansj</groupId>
<artifactId>ansj_seg</artifactId>
<version>5.1.6</version>
</dependency>

分词的使用

实现索引模块

Parser: 读取下好的文档,然后解析文档内容,完成索引的制作

一个文件的扩展名,都是随便起的,一般代表文件的格式

解析获取到的html

1> 解析html标题

我们要想办法把html后缀名给去除

去除

完成

java计算长度

2> 获取url

广告结果会根据点击计费

自然搜索结果,需要根据点击来优化用户体验

Java API 文档,有俩份

1. 线上文档

2. 线下文档

我们希望用户点击线下文档的搜索结果,就能够跳转到线上的文档页面

具体代码实现

有/和\,但是因为浏览器的容错能力很强 ,因此是每关系的

3> 解析正文

我们只关心内容,不要html标签

我们需要去掉标签

此时我们可以用正则表达式来去标签,正则表达式是一种计算机中进行字符串处理/匹配 常用的字段

核心就是通过一些特殊符号来描述字符串的特征, 然后看某个字符串是否符合这些特征

此时我们使用更加简单的方式来实现

html都是<>包裹起来的

如果找到<直到>为止,就不把它放在内容里面(使用StringBuilder) 

关于读取字符的操作

针对这个,我们发现有很多的换行操作,目的是为了生成描述信息,不应该有很多的换行(内容本身不应该有换行,此时要去除换行

解决方法: 多加一个判断语句

小总结: 

构建正排序索引和倒排索引

索引大小就是设置的DocId

正排索引实现: id ->doc对象

倒排索引: 词->文档id

需要进行分词

权重值: 描述词和文档的相关性,我们此时通过词出现的次数表示相关性

 计算权重的公式: 标题1次抵文章出现10次

迭代这些公式的方法

"小流量实验"

是否忽略大小

梳理倒排索引的步骤

什么时候使用for each

构建文档出现的问题

 

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>

json格式

反序列化: 把串转为对象

创建匿名内部类的实例的目的

计算保存和加载的时间

paeser和Index的关系

生成文件的数据

衡量加载时间

性能优化

找到性能瓶颈(那个部位耗时最高)

说明遍历的耗时最多

把刚刚单线程的代码改成多线程

单线程: 18秒

提交任务操作很快,保存所有可能会有线程安全的问题

分析过程

保证在save之前把任务都处理完成

考虑线程安全: 多个线程是否同时使用公共的东西,尝试修改同一个对象, add里面有操作公共的对象

索引大小....

此处使用加锁的方式来解决线程安全问题

修改版本

我们分别操作正排和倒排是不会产生锁竞争的,因此我们不需要对整个index对象进行枷锁,我们对各种的正排和倒排进行加锁即可,让并发最大化

最终结果:使用八个线程来执行,只要7秒

放多少个线程合适?

细节问题

1> 守护线程

通过线程池创建的线程, 不是守护线程

当main方法执行完了,这些线程任然在工作(任然在等待)

程序依然在执行(进程没有退出)

进程顺利结束

2> 第一次制作索引的时候,速度很慢,第二次很快.关机后再第一次制作, 又会很慢..

主要是parsContent里面有读文件的操作,开销很大

猜想,是否开机之后,首次运行读取文件速度特别慢?

我们第一次运行

后面再运行

差距很大,主要是缓存的问题

解决从磁盘读文件速度慢的问题,

提前使用缓存区, 把数据进行缓存

验证索引加载逻辑

小结:

实现Parse类

实现Index类

核心方法

实现搜索模块

调用搜索模块,来搜索的核心操作

1. 分词

2. 触发

3. 排序

4. 包装结果

构造摘要: 包含正文的查询词或者查询词的一部分

生成的思路: 获取所有的查询词的分词结果, 遍历分词结果, 看哪个结果在正文中出现

正文转小写

还有个问题

因此在生成描述的时候,要找到整个词都是匹配的,而不是一个词的一部分,保证word在内容中是独立的一个词

实现

我们的HTML里面js代码,因此去标签后,js也在倒排索引里面了

去除js里面的内容,使用正则表达式, 特殊字符串 描述了一些匹配规则

. 表示匹配一个非换行字符

*表示前面的字符可以出现若干次

去掉script标签和内容,去除普通的标签(不去掉内容)? 表示非贪婪匹配: 匹配到符合条件的最短结果

结果空格太多 了

我们依然使用正则,把多个空格合并为一个空格

然后进行搜索测试,发现没有js的代码了

小结:

实现了search方法

实现Web模块

提供一个web接口, 最终以网页的形式,把我们的程序呈现给用户

前端+后端

约定前后端的接口

此处我们只需要实现搜索接口即可

首先基于sevlet来进行实现

引入依赖

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

search里面是当前搜索引擎的核心代码逻辑, 构造索引, 实现搜索逻辑

api里面实现的是sevlet的代码

实现前端页面,实现搜索页

使用ajax前后交互的常用手段

拼接传过来的数据

对应进行拼接

出现的一些问题

针对内容太多,超出了屏幕

点击后, 搜索结果页被目标页面替换问题:在a标签那里设置_blank属性

俩次搜索结果混合在一起的问题

把内容的分词进行标红

1. 修改后端代码, 把查询词加上标记,套上<i>标签

2. 针对<i>设置样式,比如给<i>字体颜色设置红色

500异常

当 array list的时候,分词结果还加上了空格,因此会搜出很多其他没有用的东西

此处使用暂停词: 高频,但是没有意义的内容

停用词是哪些?

把暂停词表加载到内存里面

然后读取到hashset里面,然后遍历我们的分词,然后使用contains方法,判断在不在暂停词的表里面

此刻其实还有问题, 就是边界问题,我们之前是 " "查询词" "来保证分词的独立性,但是 查询词. 并没有排除,此时还是使用正则表达式\b

indexOf 不支持正则,但是replaceAll支持, 我们把所有的\\b都替换成空格,然后我们再进行查找

然后package-use就成功把array)给标红了

显示搜索结果的条数

我们修改前端

然后我们发现,同一个文档可能会出现俩次(一个文档既有array又有list)

这个就要和之前算权重的公式有关

去重,权重的合并步骤

分析俩路归并

分析多路归并

建立堆: 完全二叉树,小根堆

权重合并

先从若干行拿到最小的值,然后和上次插入的值一样,然后判断是不是一个文档,是一个文档就进行权重的合并,不是就进行插入

最后搜索结果

总结


http://www.kler.cn/a/588078.html

相关文章:

  • Gluten 项目贡献指南
  • 行为模式---模版模式
  • S32K144入门笔记(十):TRGMUX的初始化
  • 区块链知识点2
  • 3.水中看月
  • IP 地址
  • 一级运动员最小几岁·棒球1号位
  • 使用OpenResty(基于Nginx和Lua)优化Web服务性能
  • k8s系统学习路径
  • C语言之 条件编译和预处理指令
  • ospf单区域
  • 【MySQL】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法
  • 【leetcode hot 100 108】将有序数组转换为二叉搜索树
  • 英语面试常见问题
  • 前缀和算法第一弹(一维前缀和和二维前缀和)
  • 【环境配置】windows下vscode下无法激活conda环境、创建虚拟环境报错
  • 算法题刷题方法记录(蓝桥杯、Leetcode)
  • Spring MVC拦截器中的责任链模式深度解析
  • 深度探索DeepSeek部署的安全底线
  • 第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组题目试做(上)