【从0做项目】Java文档搜索引擎(9)烧脑终章!

ops/2025/2/24 21:55:56/

   

阿华代码,不是逆风,就是我疯

你们的点赞收藏是我前进最大的动力!!

希望本文内容能够帮助到你!!

目录

文章导读

零:项目结果展示

一:导入

二:问题引入

1:情景引入

2:思考

3:处理设计

(1)问题总结

(2)设计

(3)核心思路

三:代码讲解

1:search方法

2:mergeResult

(1)Pos定位类

(2)看图说话

(3)步骤拆解

四:前后优化结果对比


文章导读

阿华将发布项目复盘系列的文章,旨在:

1:手把手细致带大家从0到1做一个完整的项目,保证每2~3行代码都有详细的注解

2:通过文字+画图的方式,对项目进行整个复盘,更好的理解以及优化项目

3:总结自己的优缺点,扎实java相关技术栈,增强文档编写能力

零:项目结果展示

项目目前已经上线,小伙伴们可以进行使用!!!

Java 文档搜索

简述:在我的搜索引擎网站,用户进行关键字搜索,就可以查询到与这个关键字相关的java在线文档,(包含标题,关键字附近的简述,url),用户点击标题,即可跳转到相关在线文档,适用于JDK17版本。

一:导入

在前文(8)中我们使用停用词表对用户的搜索词句进行了过滤,并且在后端处理正文描述的时候使用正则表达式进行优化,让返回结果更加合理。本篇文章将会有点烧脑~

二:问题引入

1:情景引入

这里我们同样搜索array空格list

惊奇的发现array这个文档返回了两次,什么鬼~~!! 

2:思考

为什么一个文档会返回两次。想后端处理逻辑,我们拿到array这个词,在倒排索引中返回一堆docId;再拿到list这个词,再在倒排索引中返回一堆docId

注:(这里拿到的其实是一个集合,里面有好多Weight对象,对象里包含docId和weight权重,这里这么说是方便大家理解)

思考:那有没有一种可能就是说,一个文档中既包含array,又包含list,所以这个文档被查到了两次,就返回给前端两遍,显然,这种情况是非常有可能的!! 

不多bb直接上图,这里图解可能更清楚。

3:处理设计

(1)问题总结

①一个文档不能出现两次 

②像Array.html这样的文档,同时包含多个分词结果,意味着这个文档的“相关性”更高——所以就应该提高这个文档的权重!!

设计

(2)设计

①去重:把多个分词结果触发出来的文档,按照docId进行去重

②权重合并

(3)核心思路

①把分词结果进行排序处理(按照docId升序排序)

②对于docId相同的情况,进行权重的相加

注意:这里的分词结果可能不止两个,当有多个的时候,每一个分词都对应一个list集合,这里就是多路数组的归并了。

这里不理解的看下面这个图文字

不多bb上图理解

三:代码讲解

1:search方法

不要捉急,我们一点点的看代码

在search方法中我们使用mergeResult方法来进行合并,这里的参数传递,可以理解成把所有查到的docId相关文档作为参数进行传参,实际上传的是一个双重集合,这个集合中装的全都是Weight对象

java">    public List<Result> search(String query){//1:对query分词List<Term> oldTerms = ToAnalysis.parse(query).getTerms();//未过滤的分词结果集合List<Term> terms = new ArrayList<>();//过滤后的分词结果集合//针对分词结果,使用暂停词表进行过滤for(Term term : oldTerms){if(stopWords.contains(term.getName())){continue;}terms.add(term);}//2:对分词查倒排List<List<Weight>> termResult = new ArrayList<>();
//        List<Weight> allTermResult = new ArrayList<>();for (Term term : terms){String word = term.getName();List<Weight> invertedList = index.getInverted(word);//如果查不到就返回一个nullif(invertedList == null){continue;}
//            allTermResult.addAll(invertedList);//把集合中所有Weight对象都扔到allTermResult中termResult.add(invertedList);}//3:[合并]对多个分词结果处发出的相同文档,进行权重合并List<Weight> allTermResult = mergeResult(termResult);//4: 按权重降序排序allTermResult.sort(new Comparator<Weight>() {@Overridepublic int compare(Weight o1, Weight o2) {return o2.getWeight() - o1.getWeight();//降序排列}});//5:查正排,构造出想要的Result,返回结果List<Result> results = new ArrayList<>();for(Weight weight : allTermResult){//对每一个Weight都构建result,可能最后的结果会很多,但是用户一般只看第一页查询出来的信息,一般懒得翻页DocInfo docInfo = index.getDocInfo(weight.getDocId());//获取当前Weight对应的文档信息Result result = new Result();result.setTitle(docInfo.getTitle());result.setUrl(docInfo.getUrl());
//            result.setDesc(docInfo.getContent());//很明显把正文全部返回不合理result.setDesc(GenDesc(docInfo.getContent(),terms));//搞个正文简述,这个词前60个字符为起始,往后截取160个results.add(result);}return results;}

2:mergeResult

(1)Pos定位类

用来描述,我们Weight对象所在的位置

java">    static class Pos{public int row;public int col;public Pos(int row , int col){this.row = row;this.col = col;}}

(2)看图说话

搞一个优先级队列,比较规则就是,docId值更小的往里面放

(3)步骤拆解

①对每一路按docId的升序给Weight对象排个序

②new一个集合用来存放最后的Weight对象的合集

③把每一行的第一个元素放进队列中(初始化)

优先级队列的比较规则是docId升序排列,放的是Pos对象也就是Weight对象的位置!!

⑤当队列不为空时,循环弹出元素Pos,找到对应的Weight对象,将这个Weight对象与我们target集合中最后一个位置的Weight对象进行对比看是不是同一个对象,若不是则直接加入集合,若是则合并权重。

⑥指针移动

喵喵喵~~妙脆角!跟着我的注解,看着图,敲一遍代码会更清楚内部的一个逻辑!

java"> private List<Weight> mergeResult(List<List<Weight>> source) {//把多路合并成一路//1:先给每一路按升序排个序for (List<Weight> curRow : source){curRow.sort(new Comparator<Weight>() {@Overridepublic int compare(Weight o1, Weight o2) {return o1.getDocId()- o2.getDocId();}});}//2:借优先级队列合并多路List<Weight> target = new ArrayList<>();PriorityQueue<Pos> queue = new PriorityQueue<>(new Comparator<Pos>() {@Overridepublic int compare(Pos o1, Pos o2) {Weight w1 = source.get(o1.row).get(o1.col);//用下标找到Weight对象Weight w2 = source.get(o2.row).get(o2.col);return w1.getDocId() - w2.getDocId();}});//2.1:初始化队列——把每一行第一个元素放到队列当中for(int row = 0 ; row < source.size() ; row++){queue.offer(new Pos(row,0));}//2.2:循环取队首元素(也就是当前若干行中最小的元素)while(!queue.isEmpty()){Pos curMinPos = queue.poll();Weight curWeight = source.get(curMinPos.row).get(curMinPos.col);//2.3:检查当前的Weight对象,与上一个插入到target中的对象是否是相同的对象,这里可以用Weight对象中的docId作为比较依据if(target.size() > 0){Weight lastWeight = target.get(target.size()-1);if(lastWeight.getDocId() == curWeight.getDocId()){//文档id若相等则合并int weightSum = lastWeight.getWeight() + curWeight.getWeight();lastWeight.setWeight(weightSum);}else{//文档id不相等就直接入targettarget.add(curWeight);}}else{//若当前的target为空,就直接加入target.add(curWeight);}//2.4:考虑移动光标,当前元素处理完了之后,要把对应的这个元素光标往后移动,取这一行的下一个元素Pos newpos = new Pos(curMinPos.row , curMinPos.col+1);if(newpos.col > source.get(newpos.row).size() - 1){//说明光标已经超出这一行的范围了,到达末尾了,这一行就处理完了continue;//直接进入下一次循环}//否则把新的坐标扔到队列当中queue.offer(newpos);}return target;}

四:前后优化结果对比

暴减500条结果,说明有200多个结果都是重复的。

至此Java文档搜索引擎博客讲解就结束了,这里的图解和测试,花费了阿华很大的精力,希望这个系列能够帮助到你~~塔塔开!


http://www.ppmy.cn/ops/161049.html

相关文章

【DeepSeek】本地部署,保姆级教程

deepseek网站链接传送门&#xff1a;DeepSeek 在这里主要介绍DeepSeek的两种部署方法&#xff0c;一种是调用API&#xff0c;一种是本地部署。 一、API调用 1.进入网址Cherry Studio - 全能的AI助手选择立即下载 2.安装时位置建议放在其他盘&#xff0c;不要放c盘 3.进入软件后…

技术分享:MyBatis SQL 日志解析脚本

技术分享&#xff1a;MyBatis SQL 日志解析脚本 1. 脚本功能概述2. 实现细节2.1 HTML 结构2.2 JavaScript 逻辑 3. 脚本代码4. 使用方法4.1 示例 5. 总结 在日常开发中&#xff0c;使用 MyBatis 作为持久层框架时&#xff0c;我们经常需要查看 SQL 日志以调试和优化查询。然而&…

Docker入门及基本概念

让我们从最基础的概念开始逐步理解。假设你已经准备好了docker 环境。 第一步&#xff0c;让我们先通过实际操作来看看当前系统中的镜像(images)和容器(containers)状态&#xff1a; docker images # 查看所有镜像 docker ps -a # 查看所有容器&#xff08;包括未运行…

postman调用ollama的api

按照如下设置&#xff0c;不需要设置key 保持长会话的方法 # 首次请求 curl http://localhost:11434/api/generate -d {"model": "deepseek-r1:32b","prompt": "请永久记住&#xff1a;110&#xff0c;1-12&#xff0c;之后所有数学计算必…

QT 基础知识点

1.基础窗口类QMainWindow qDialog Qwidget 随项目一起创建的窗口基类有三个可选QMainWindow qDialog Qwidget 1.1 Qwidget 是所有窗口的基类&#xff0c;只要是他的子类&#xff0c;或子类的子类&#xff0c;都具有他的属性。 右键项目 Add New -> Qt qt设计师界面类&am…

FTP 实验(ENSP模拟器实现)

FTP 概述 FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是一种用于在网络上进行文件传输的标准协议。它允许用户在两台计算机之间上传和下载文件。 1、FTP采用客户端-服务器模型&#xff0c;客户端通过FTP客户端软件&#xff0c;连接到FTP服务…

重学SpringBoot3-Spring Retry实践

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞??收藏评论 重学SpringBoot3-Spring Retry实践 1. 简介2. 环境准备3. 使用方式 3.1 注解方式 基础使用自定义重试策略失败恢复机制重试和失败恢复效果注意事项 3.2 编程式使用3.3 监听重试过程 监…

计算机毕业设计SpringBoot+Vue.jst0甘肃非物质文化网站(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…