# 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`(content 含 `` 高亮)完全不变,前端零改动。 ## 二、新增内容(全新代码) **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`,批量插入走 `saveAll`。 **4. 写入服务 `module/search/service/SearchDocIndexService.java`(核心)** - `add(SearchDoc)`:按 caseId(取自 `CaseContextHolder`)攒批,满 1000 条 `saveAll`;上下文缺失抛异常。 - `flush()`:提交尾部不满批 + `IndexOperations.refresh()`(替代原 forceMerge 时点)。 - `deleteByFileIds(List 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 ``/``,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 清洗链路/前端零逻辑改动。