API Search Engine

搜索引擎

  • 标题
  • 描述
  • 展示url

跳转结果称为 **落地页

  1. 获取无数网页 > 爬虫程序 发送无数Http请求,相当于一个HTTP的客户端
  2. 为了提高查询速度 :查询词和当前网页进行匹配 则需要 倒排索引 \color{red}{倒排索引} 倒排索引这个特殊的数据结构
  3. 正排索引:文档id -> 文档内容
  4. 倒排索引:词 -> 文档id列表

一个针对java文档的搜索引擎

  1. 全站搜索 对整个互联网上所有的网站
  2. 站内搜索 只针对某给网站内部内容进行搜索

基于本地的离线文档制作索引,用户在搜索结果页面点击具体的搜索结果的时候,自动跳转到在线文档上

模块划分:

  1. 索引模块
    1. 扫描下载的文档,并分析,构建正排索引+倒排索引,并把索引保存到文件中
    2. 加载索引,提供API
  2. 搜索模块
    实现搜索的完整过程
  3. 实现简单的web程序,用网页的形式和用户进行交互



对于用户输入的内容,往往是一句话。但在搜索时,可以看到现有的搜索引擎是会根据句子中的词语进行网页匹配的,所以此软件也需要实现分词功能。 但分词是较为复杂的,尤其是像中文这样的语言,所以为了简便起见,也为了是该软件具有一定的可扩展性,采用第三方库实现分词。(这也更接近实际的开发)

分词的原理

  1. 基于词库
    把所有“词”进行穷举,放入一个词典中,之后根据句子进行对应查询即可
  2. 基于统计
    收集海量的“语料库” 比如已有的文章、资料。 > 人工标注分词结果/直接统计 > 哪些字频繁连接使用

ansj库可以实现分词* 支持中英文
ansj会加载一些词典文件,通过词典文件能够加快分词速度和准确率 (词典中可以包含最新的词语)
没这些词典文件,依然可以顺利运行


Parser类###

实现制作索引的过程:
读取下载的文档,并进行解析,完成索引的制作 

最后展示的效果是标题、描述、URL

  1. 其中描述可认为是正文的一个摘要,直接得到描述是困难的,先得到正文,再进后续过程

    完整的HTML文件=HTML标签+内容,所以去掉标签,就是内容。

    去标签:即去除<……>,在HTML里,如果内容中出现<>,则分别转义为< less than it
    > greater than it

  2. 标题在实际的搜索引擎中,也是一个超链接,即是一个a标签,在每个搜索结果的底部又会显示 一个URL,这两个是不一样的,但在此项目中为了方便起见,设置为相同的。

    期望的结果:用户点击搜索结果,就能跳转到对应的线上文档的页面。为此修改本地的URL为线上文档的URL,即进行字符串拼接

    在线文档的URL为 https://docs.oracle.com/javase/17/docs/api/

    因为搜索引擎会统计网页被点击的次数,所以标题对应的URL一般会与搜索引擎有关

Index类

为了实现快速搜索,就需要有相应的索引

索引
常用的就是正排索引和倒排索引,设计的数据结构在被查询时最好复杂度只有O(1)
所以正排索引用ArrayList,倒排索引用哈希

倒排索引的构建的步骤:
首先得知道 某个文档中有哪些词,即分词。
1. 对标题进行分词
2. 对内容(即正文)进行分词
根据分词的结果,将文档加入相应的倒排索引的value中 ,其中weight 简单处理为词出现的次数 标题中出现的词的权重理应高于正文中出现的词。实际中搜索引擎通过点击率衡量weight,点击率为 点击次数/展示次数

索引的构造过程是十分耗时的,因为设计了很多的运算,单个索引构造的时间虽然不长,但索引本身的数量是十分庞大的。所以同样的索引不可能反复构造。应该构造好了存入磁盘中。也就是是说,服务器每次启动时,不同时构建索引,而是直接从硬盘中加载已经构造好的索引。否则服务器启动是极慢的。构造的过程是单独执行的。

把索引文件以字符串的形式进行保存,即序列化
将字符串解析为特定结构的数据,即反序列化
借助JSON格式实现

在加载的时候,借助了Jackson中的ObjectMapper里的readValue方法。

forwardIndex = objectMapper.readValue(forwardIndexFile, new TypeReference<ArrayList<DocInfo>>() {});

forwardIndexFile 指定带读取的文件,
new TypeReference<ArrayList<DocInfo>>() {} 表明读到的数据按照什么类型进行解析

要将字符串转换为ArrayList<DocInfo>类型的对象 ,Jackson提供了一个辅助的工具类 TypeReference< ……>,这个类的泛型参数就是转换的目标类型。因为Java的语法规定,参数不能类型,只能是一个实例。 创建一个匿名内部类的实例,是为了传递类型信息。

衡量代码的执行时间的简单方式:
开始执行前,获得一个时间戳
执行结束后,获取一个时间戳


大数据的量级至少也在TB级
几十MB的文本文件,可以用记事本打开,但是容易卡死。按理说这么点数据全部加入内存是很快的,但记事本打开的效果极差。用VScode很好


通过实际运行,发现运行时间较长,所以要性能优化。
首先就要确定性能瓶颈是什么,也就是通过前后加时间戳这种简单的方式确定瓶颈段

此程序的性能瓶颈为循环遍历文件上,
每循环一次,就对一个文件进行处理:读文件 + 分词 + 内容解析 主要还是CPU的运算占据很多时间。

单个线程的情况,这些任务都是串行执行的,所以要考虑并发编程


IDEA在控制台展示的内容是来自内从中的一个缓冲区的,有一定大小,要显示的太多,位于前面的内容就会丢弃


搜索模块###

调用索引模块,完成搜索

  1. 分词,针对用户输入的查询词进行分词
  2. 触发,对每个分词结果,在倒排索引中查询,找到相关文档
  3. 排序,针对上述触发的结果,按照相关性降序排序
  4. 包装结果,根据排序结果,依次去查正排,获取每个文档的详细信息,并按照一定的数据结构进行返回

描述 是正文的一段摘要,需要包含查询词/查询词的一部分

对于某个文档来说,不一定包含所有的分词结果

在文档中找到分词结果的位置,以这个为中心,向前30字符,向后30字符,作为整个描述

Web模块

约定前后端的的结构
请求
GET /searcher?query=[查询词] HTTP/1.1

响应
HTTP/1.1 200 OK

[
{
title: “标题1”,
url: “url1”,
desc:“描述1”,
}
]

为了实现前端展示上,查询词会被标红,则在后端对于查询词加上标记,套上<i>标签,在前端对<i>设置样式即可

之前的分词导致暂停词也成为了倒排索引的查询对象

暂停词:停用词 即高频 但 不具备查询意义的词
a is have 等

之后是搜索程序加载停用表,针对分词结果,在停用词表中进行筛选,如果在就干掉


多个分词结果被同一个文档包含,则表明该文档的相关性更高,则Weight权重更大

采用merge-sort ,按照docID升序排序,之后合并

为了找打最小值,使用堆排序

正排索引,倒排索引,暂停词三个文件


总结#

1. 索引模块###

  1. Pareser 类:制作索引的流程
  2. Index 类 : 实现索引的树结构

2. 搜索模块###

Sercher 类 : 完成搜索的流程
1. 调用Index 类 ,实现正排倒排的查询
2. 生成描述、标红、文档合并(去重,合理利用权重)等功能,使得展示更合理

3. Web模块###

Servlet/Spring Boot 两个后端版本的服务器程序
HTML/CSS/JS 搜索界面

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值