【项目】基于正倒排索引的Java文档搜索引擎

一. 项目简介

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

文档:每个待搜索的网页;

正排索引:通过文档id查找文档内容;

倒排索引:通过 词 查找文档id

  1. 索引模块

1)扫描下载到的文档,分析文档内容,构建出正排索引+倒排索引,并把索引内容保存到文 文件中;

2)加载制作好的索引,并提供一些API实现查正排和查倒排的功能

  1. 搜索模块

调用搜索模块,实现完整的搜索过程

输入:用户的查询词;

输入:完整的搜索结果(包含多条记录,每个记录有标题,描述,展示URL,点击能够跳转)

3.web模块

实现简单的web程序,能够通过网页形式与用户进行交互

二. 索引模块

分词:把完整的句子分成多个词,可通过现成的第三方库来完成分词

例如:一个 / 人 / 感觉 / 好 / 孤单

分词原理:

1.基于词库

尝试把所有的词进行穷举,把穷举结果放到词典文件中,依次取句子中的内容,每个一个/两个...词查一下;

2.基于原理

收集"语料库",进行人工标注,进一步知道哪些词在一起的概率比较大
实现索引模块:创建一个类,通过这个类完成制作索引的过程

Parse:读取下载好的文档,解析文档的内容,并完成索引的操作

模块索引小结:

1.1 实现了一个Parser 类

1)通过递归方式枚举出所有的 html 文件;

2)针对每个 html 进行解析操作:

a)标题:使用文件标题;

b)URL:基于文件路径进行简单拼接(离线文档和线上文档路径的关系);

c)正文:核心操作:去标签,使用 < > 作为是否要进行拷贝数据的开关;

3)把解析结果放到 Index 类中(addDoc)

注意:html 要求内容中出现的 < 使用 &lt; 代替,> 使用 &gt; 代替

Java标准库中,既提供了按照字节读取的类(FileInputStream),也提供了按照字符读取的类(FileReader)

保证所有文档处理完毕再保存索引:CountDownLatch

CountDownLatch 类似于跑步比赛的裁判,所有选手都撞线就认为比赛结束了

  1. 在构造 CountDownLatch 的时候指定一下比赛选手的个数;

  2. 每个选手撞线,都要通知一下 countDown();

  3. 通过 await() 来等待所有的选手撞线完毕

守护线程 VS 非守护线程

守护线程:此线程的运行状态不会影响到进程结束

非守护线程:此线程的运行状态会影响到线程结束

之前学过的线程创建手段,默认创建非守护线程,需要通过 setDaemon 方法手动设置为守护线程

1.2 实现了一个 Index 类

核心属性:

1)正排索引,ArrayList<DocInfo> ,每个 DocInfo 表示一个文档,文档中包含了id,title,url,content;

2)倒排索引,HashMap<String, ArrayList<Weight>>,每个键值对,表示这个词在哪些文档中出现过。Weight 包含文档id和权重信息

核心方法:

1)查正排,直接按照下标来取 ArrayList<DocInfo> 中的元素即可;

2)查倒排,直接按照 key 来取 HashMap<String, ArrayList<Weight>> 中的 value 即可;

3)添加文档,Parser 类在构建索引的时候调用该方法:

a)构建正排,构造 DocInfo 对象,添加到正排索引末尾;

b)构建倒排,先进行标题和正文分词,统计词频,遍历分词结果,更新倒排索引中对应的倒排拉链,注意其中的线程安全问题;

4)保存索引,基于 json 格式把索引数据保存到指定文件中;

5)加载索引,基于 json格式对数据进行解析,把文件中的内容读出来,解析到内存中

三. 搜索模块

调用索引模块,完成搜索的核心过程

  1. 分词:针对用户输入的查询词进行分词(用户输入的查询词可能是一句话);

  2. 触发:拿着每个分词结果去倒排索引中查找,找到相关性的文档(调用 Index 类中的查倒排方法);

  3. 合并:针对多个分词结果触发出的相同文档,进行权重合并;

  4. 排序:针对上面触发出来的结果,进行排序(按照相关性降序排序) ;

  5. 包装结果:根据排序后的结果,依次去查正排,获取到每个文档的详细信息,包装成一定结构的数据返回

生成描述(正文摘要)的思路:

获取到所有查询词的分词结果,遍历分词结果,看哪个结果在正文中出现。

针对这个被包含的分词结果,去正文中查找,找到对应的位置,以这个位置为中心往前截取60字符,作为描述的开始,再从描述开始截取160个字符,作为整个描述。

正则表达式:

. 表示匹配一个非换行字符(不是 \n,不是 \r)

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

.* 表示匹配非换行字符出现若干次

去掉普通标签(不去除内容):<.*?>

去掉 script 标签和内容:<scrip.*?>(.*?)</script>

? 表示"非贪婪匹配",匹配符合条件的最短结果;不带 ? 表示"贪婪匹配",匹配符号条件的最长结果

四. web模块

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

前端(HTML+CSS+JS)+后端(Java,Servlet / Spring)

请求:

GET /searcher?query=[查询词] HTTP/1.1

响应:

HTTP/1.1 200 OK

{ title: "这是标题", url: "这是url", desc: "这是描述" } { } ......

.ajax: 是一个变量名,是 jquery 库提供的一个内置的对象的变量名,jquery 中的函数与方法都是 对象提供的。有的语言(Java/JS)允许 作为变量名,有的语言(C/C++)不允许。

遇见的bug:

内容太多,超出屏幕:通过CSS属性,设置 overflow: auto,让内容都局限在 .container 这个 div 内部滚动,而不是整个页面滚动。

点击后搜索页面被目标页面替换:给 a 标签加上 target="_blank" 属性即可。

实现标红逻辑:

1.修改后端代码,生成搜索结果的时候,给包含查询词的部分加上一个标记,例如套上一层 <i> 标签;

  1. 在前端代码中针对 <i> 标签设置样式,然后浏览器根据样式进行显示。

处理停用词:

停用词:高频但没意义的内容,如 a,is,have ...... 这类词不应该参与触发。

当查询词为 "array list" 时,分词结果为 "array"," ","list" 。

因此代码中会拿 空格 来查询倒排索引,所以要处理这类词。

重复文档问题:

把多个分词结果触发出的文档,按照 docID 进行去重,同时进行权重的合并。

去重的核心思路:

数据结构中有合并两个有序链表,此处可以按照类似思路合并n个数组,先把分词结果进行排序处理(按照 docID 升序排序),再进行合并,根据 docID 值相同的情况做权重相加

五. 部署到云服务器

  1. 准备好前提环境

先拥有一个云服务器

给云服务器装好系统,如 Centos7

安装一些依赖的程序,如 jdk,tomcat

  1. 把程序依赖的数据拷贝到云服务器上

注意:搜索程序运行时,不光代码本身,项目依赖的正排索引,倒排索引,暂停词文件, 都需要拷贝

  1. 调整代码路径

目的是使代码能够找到云服务器上的文件

  1. 将项目打包成 jar 包上传
相关推荐
波波七2 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
李剑一2 小时前
告别冗余代码!Cesium点位图标模糊、重叠?自适应参数调优攻略,一次封装终身复用!
前端·vue.js·cesium
sz_denny2 小时前
chrome os 如何进入开发者模式
前端·chrome
踩着两条虫2 小时前
🔥 实测对比:VTJ.PRO凭啥让头部企业放弃自研低代码?
前端·vue.js·ai编程
book123_0_992 小时前
Spring boot创建时常用的依赖
java·spring boot·后端
帐篷Li2 小时前
【BBF系列协议】USP/TR-369 Agent 开发计划
开发语言·python
重庆小透明2 小时前
【java基础内容】ConcurrentHashmap源码万字解析
java·开发语言