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

基于倒排索引的 Java 文档搜索引擎(三)

专栏:基于正倒排 Java 文档搜索引擎

个人主页:手握风云

目录

一、Web 模块

1.1. 整体架构

1.2. 后端 Web 接口实现

1.3. 处理停用词

1.4. 多路归并


一、Web 模块

Web 模块的核心作用是将后端搜索能力封装为Web 接口,并提供可视化前端页面,让用户通过浏览器完成搜索、查看结果、跳转官方 Java API 文档的完整操作,是整个搜索引擎的交互入口。核心流程:前端发起搜索请求 → 后端 Controller 接收并调用搜索模块 → 返回 JSON 结果 → 前端渲染展示。

1.1. 整体架构

  • 后端:SpringBoot Controller 层,提供搜索接口,对接搜索模块
  • 交互:GET 请求传参,JSON 格式返回数据
  • 优化:搜索结果关键词标红、样式美化、新窗口跳转文档

1.2. 后端 Web 接口实现

核心基于 SpringBoot Web 场景,使用 @RestController 声明控制器(返回 JSON / 字符串,非页面跳转);接口:/searcher,GET 请求,参数query(搜索关键词)。

package com.yang.java_doc_searcher.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.yang.java_doc_searcher.searcher.DocSearcher; import com.yang.java_doc_searcher.searcher.Result; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class DocSearchController { private static DocSearcher searcher = new DocSearcher(); private static ObjectMapper objectMapper = new ObjectMapper(); @GetMapping(value = "/searcher", produces = "application/json;charset=utf-8") @ResponseBody public String search(@RequestParam("query") String query) throws JsonProcessingException { List<Result> results = searcher.search(query); return objectMapper.writeValueAsString(results); } }

1.3. 处理停用词

如果我们输入的 query 为“Array List”,我们会发现查询的摘要里面空格也被算进了分词结果集中来执行倒排索引查询。有些无意义高频词,如 is、the、a、have 进行剔除,可以提升搜索速度与结果精准度。因此我们需要在网上找到现有的停用词表,再通过程序把停用词表加载到内存中。我们可以使用哈希表来存储这些停用词,再针对分词结果在哈希表中进行筛选。

private static final String STOP_WORD_PATH ="D:\\doc_search_index\\stop_word.txt"; private HashSet<String> stopWords = new HashSet<>(); // 停用词加载方法 private void loadStopWord() { try (BufferedReader bufferedReader = new BufferedReader(new FileReader(STOP_WORD_PATH))) { while (true) { String line = bufferedReader.readLine(); if (line == null) { // 读取文件完毕 break; } stopWords.add(line); } } catch (IOException e) { e.printStackTrace(); } }

1.4. 多路归并

当输入多词查询时,同一文档被多次匹配」的问题,将多个词的权重累加,让同时包含多个查询词的文档排名更靠前。原理类似N 个有序数组的归并,用优先队列实现高效归并。

private List<Weight> mergeResult(List<List<Weight>> source) { // 对每个内部列表按DocId进行排序 for (List<Weight> curRow : source) { curRow.sort(new Comparator<Weight>() { @Override public int compare(Weight o1, Weight o2) { // 比较两个Weight对象的DocId,返回差值用于排序 return o1.getDocId() - o2.getDocId(); } }); } // 初始化目标列表和优先队列 List<Weight> target = new ArrayList<>(); // 创建优先队列,用于存储每个列表当前最小的元素位置 PriorityQueue<Pos> queue = new PriorityQueue<>(new Comparator<Pos>() { @Override public int compare(Pos o1, Pos o2) { // 比较两个位置对应的Weight对象的DocId Weight w1 = source.get(o1.row).get(o1.col); Weight w2 = source.get(o2.row).get(o2.col); return w1.getDocId() - w2.getDocId(); } }); for (int row = 0; row < source.size(); row++) { queue.offer(new Pos(row, 0)); } while (!queue.isEmpty()) { Pos minPos = queue.poll(); Weight curWeight = source.get(minPos.row).get(minPos.col); // 处理相同DocId的Weight合并 if (target.size() > 0) { Weight lastWeight = target.get(target.size() - 1); if (lastWeight.getDocId() == curWeight.getDocId()) { // 如果DocId相同,合并权重 lastWeight.setWeight(lastWeight.getWeight() + curWeight.getWeight()); } else { // 如果DocId不同,添加到目标列表 target.add(curWeight); } } else { // 如果目标列表为空,直接添加当前元素 target.add(curWeight); } // 将当前元素的下一个元素加入优先队列 Pos newPos = new Pos(minPos.row, minPos.col + 1); if (newPos.col >= source.get(newPos.row).size()) { // 如果当前列表已处理完,跳过 continue; } queue.offer(newPos); } return target; }
http://www.jsqmd.com/news/700242/

相关文章:

  • 短期备考雅思必看|1-3个月冲刺选机构实测:5家对比,多次元凭什么稳赢 - 速递信息
  • Xiaomi MiMo-V2.5 系列大模型开启公测
  • Hydra:面向超级个体的分布式操作系统基座设计与实战
  • 028、工程化进阶:容错、重试与降级策略
  • JavaScript 循环机制深度解析
  • 是德科技Keysight(Agilent) N9030A PXA 信号分析仪
  • 知识库上线后检索静默失效:一次从监控盲区到分层治理的RAG故障复盘
  • 汉字转拼音工具,即输即转可多格式导出
  • 高效实现分组内跨行时间戳匹配:为每组生成布尔标记列 user_rejects
  • VSCode 2026车载调试爆发式升级:5大原生支持新特性(Adaptive AUTOSAR调试器、UDS over DoIP直连、时间敏感网络TSN时序可视化)你还没用?
  • prettier代码格式化
  • 终极游戏光标自定义工具:YoloMouse让你的鼠标指针在游戏中脱颖而出!
  • 第21章信息物理系统分析与设计
  • 液冷阀门清洁度检测设备 西恩士液冷部件源头生产厂商 - 工业设备研究社
  • Keras上采样与转置卷积:核心差异与实战应用
  • (课堂笔记)Oracle 常用函数:数值、字符串、日期处理
  • CUDA 13.3正式版发布前夜必读:AI框架厂商未公开的3大ABI断裂点(含TensorRT-10.3/ONNX Runtime 1.18兼容性矩阵速查表)
  • HeteroFlow完成主流国产GPU适配,打破国外技术垄断助力算力生态建设
  • Power BI学习笔记第10篇:实战案例 — 销售数据分析仪表板
  • 嵌入式机器人开发实战:从零到整的20个STM32F4核心示例深度解析
  • DeepSeek V4写的文章AI率高怎么降?2026年4月3步降到5% - 我要发一区
  • 如何用新蜂商城在2分钟内搭建完整的电商系统?
  • 英雄联盟国服终极换肤神器:R3nzSkin完整使用指南
  • (课堂笔记)Oracle 表关联:连接类型、数据发散、自关联、同环比计算
  • 高级安卓开发在DVR类产品中的应用与挑战
  • .NET 集成 SqlSugar、读写分离 、Redis
  • 生产级AI智能体架构实战:从原型到产品的工程化指南
  • DeepSeek V4写完用哪款降AI?2026年4月4款工具横评 - 我要发一区
  • 2026年独立站+TikTok Shop双轨策略:为什么聪明品牌不再押注单一渠道 - SocialEcho社媒管理
  • OpenCore Legacy Patcher终极指南:如何免费让旧Mac焕发新生