plan-sess_949761e8-eef2-41ae-ab35-997f58f1b966.md 6.2 KB

Lucene → spring-data-elasticsearch 全新改造计划

一、设计原则(按你的要求)

  • 彻底删除:LuceneManager、UnitSeparatorAnalyzer、pom 里 4 个 lucene 依赖、所有 Lucene 调用点,全部清除,不做兼容层、不做门面移植。
  • 全新一套:新包 com.zsjz.ai.module.search,spring-data-elasticsearch 惯用法(实体注解 + Repository + ElasticsearchOperations)。
  • 只动插入点:27 个清洗 Loader 的写入处一行换成新 API;其余清洗逻辑(DuckDB、进度回调)不碰。
  • 案件隔离:单索引 + caseId 字段,由新服务强制保障(写入时从 CaseContextHolder 注入 caseId 打标、查询/删除必带 caseId 过滤,无绕过路径;缺上下文抛异常,与现有语义一致)。
  • 批量插入:服务内部按 case 攒批(满 1000 条 → repository.saveAll() 批量提交),对外只暴露 add() / flush()。
  • 搜索接口不变:GET /search/list 的 URL、入参、返回 List<SearchDTO>(content 含 <em class="search-highlight"> 高亮)完全不变,前端零改动。

二、新增内容(全新代码)

1. 依赖与配置

  • ai-server/pom.xml:删 lucene-core / queryparser / analysis-common / highlighter(:241-267),加 spring-boot-starter-data-elasticsearch(Boot 3.5.16 托管版本,不写版本号 → spring-data-es 5.5.x + 客户端 8.18.x)。
  • application.yaml:spring.elasticsearch.uris: http://127.0.0.1:9200(可指向内网服务器)。

2. 实体 module/search/domain/SearchDoc.java

  • @Document(indexName = "zsjz_search") + @Setting(settingPath = "/es/search-settings.json")(1 shard / 0 replica;pattern tokenizer 按 \x1F 切分 + lowercase,复刻现 UnitSeparatorAnalyzer 语义)。
  • 字段:@Id id;caseId/sheetId/fileId/tableName/fileName/sheetName(Keyword);dateTime(Date, pattern yyyy-MM-dd HH:mm:ss);content(Text, analyzer=unit_separator)。与现 SearchDTO 字段一一对应,新增 caseId。

3. Repository module/search/repository/SearchDocRepository.java

  • extends ElasticsearchRepository<SearchDoc, String>,批量插入走 saveAll。

4. 写入服务 module/search/service/SearchDocIndexService.java(核心)

  • add(SearchDoc):按 caseId(取自 CaseContextHolder)攒批,满 1000 条 saveAll;上下文缺失抛异常。
  • flush():提交尾部不满批 + IndexOperations.refresh()(替代原 forceMerge 时点)。
  • deleteByFileIds(List<String> fileIds):按 caseId + fileId 的 deleteByQuery + refresh(替代原 deleteDocuments(new Term("fileId",...)))。
  • deleteByCase(Long caseId):删案时清理该案全部 ES 数据(显式化,原"索引随 workspace 目录删除"的隐式语义必须有等价物)。
  • SearchIndexInitializer(ApplicationRunner):启动时 indexOps(SearchDoc.class).createWithMapping(),索引不存在则建,幂等。

5. 查询服务 module/search/service/SearchQueryService.java

  • NativeQuery:bool(must: wildcard content = *kw*,filter: term caseId)+ dateTime 降序 + maxResults 500 + HighlightQuery(pre/post tag <em class="search-highlight">/</em>,fragmentSize 120,numberOfFragments 1)。
  • SearchHit.getHighlightFields 取高亮片段组装 SearchDTO(未命中回退原文,与现行为一致);keyword trim+lowercase 保留。
  • 异常时返回空列表的现状语义保留。

三、删改点清单(全部原有 Lucene 痕迹)

位置 改动
module/dm/clean/ 2 个抽象基类 + 25 个 Loader(27 处 LuceneManager.add(new SearchDTO(...))) 一行换 searchDocIndexService.add(new SearchDoc(...))(构造参数一致)
DmService.java:565 回调 forceMerge() 换 searchDocIndexService.flush()(外层 CaseContextHolder.runWith 保留不动)
DmService.java:826-828 deleteDocuments(Term) + forceMerge 换 deleteByFileIds(ids)
CaseInfoService.open:178-179 createWriter 整段删除(ES 无需开案建索引)
CaseInfoService.exit:192 / closeOwnedCases:223 close 删除(无需关 Writer)
CaseInfoService.delete:139 close 换 searchDocIndexService.deleteByCase(id)
AppStopEndEventListener(只调 closeAll) 整个文件删除(客户端生命周期归 Boot 管)
module/otg/service/SearchService.java(Lucene 查询/高亮) 删除,SearchController 注入新 SearchQueryService(URL/出入参不变)
LuceneManager.java、UnitSeparatorAnalyzer.java、pom 4 依赖 删除
SearchDTO / SearchController URL 不动(对外契约)

存量案件:不加重建接口,旧案件重新执行一次数据清洗即可重建 ES 索引(如后续需要再单独提)。

四、实施顺序

  1. P1 依赖/配置 + 新包全套(实体、settings JSON、Repository、IndexService、Initializer、QueryService)。
  2. P2 插入点替换(27 处)+ DmService/CaseInfoService 改点。
  3. P3 查询切换(SearchController → 新服务),删旧 SearchService。
  4. P4 删除 LuceneManager/UnitSeparatorAnalyzer/AppStopEndEventListener/依赖,全量编译清零 Lucene import。
  5. P5 文档(CODE_WIKI §6.5、AI_AGENT.md 技术栈表)+ 验证。

五、验证

  • 编译通过且全仓无 org.apache.lucene 引用(grep 清零)。
  • 有本地 ES 时跑一个门控集成测试(@EnabledIfEnvironmentVariable):启动建索引 → 批量写 → 搜索(含高亮格式)→ 按 fileId 删 → 删案清理。
  • 冒烟:开案 → 清洗 → 全局搜索弹窗(防抖/高亮/明细下钻)→ 删文件 → 删案。

六、部署与风险

  • ES 服务端 8.18.x 单节点(与 Boot 托管客户端匹配):zip 解压、内置 JDK、heap 512m、xpack.security.enabled=false(内网桌面场景)。
  • 风险:wildcard 子串查询性能与现 Lucene 同量级(单案几十万行可接受);ES 成为外部依赖,ES 不可用时清洗写入/搜索报错——按现有日志风格明确失败,不静默吞。
  • 改动面:新增 5 个类 + 1 个 JSON;删除 3 个类 + 4 个依赖;机械替换 ~32 处调用点;SearchController/SearchDTO/DuckDB 清洗链路/前端零逻辑改动。