--- title: "Easy-Es 框架深度集成-ORM 方式操作 Easysearch 的最佳实践" date: 2026-03-07 lastmod: 2026-03-07 description: "深入讲解如何使用 Easy-Es 框架以类 MyBatis-Plus 的 ORM 方式深度集成 Easysearch,涵盖索引管理、条件构造、高亮聚合、SQL 查询等核心功能,并结合告警日志检索场景给出生产级最佳实践,帮助团队快速实现搜索功能的工程化落地。" tags: ["Easy-Es", "ORM", "最佳实践"] summary: "在很多企业项目里,“搜索”早已不是锦上添花,而是支撑 日志检索、告警排查、运营分析、内部统一搜索 的核心底座。 但在工程实践中,团队往往会同时面临两类现实问题: DSL 足够灵活,但代码量大、可读性差、维护成本高 索引创建/变更、Mapping 升级、数据迁移……一堆“脏活累活”容易出错 国产化/信创落地时,希望替换底层引擎,同时又希望尽量不改业务代码 这也是 Easysearch(分布式近实时搜索与分析引擎) 与应用侧生态结合的价值所在:在保持兼容与工程效率的同时,提供更安全稳定、易维护的搜索底座。 而在应用侧,Easy-Es for Easysearch(Easy-Es 2.1.0-easysearch) 给了一个更工程化的答案: 用类似 MyBatis-Plus 的 ORM 方式,完成 Easysearch 的 CRUD、条件构造、高亮、聚合、SQL 查询,让业务研发“像写数据库代码一样写搜索”。 本文不会把重点放在“手把手搭项目”,而是围绕 深度集成 + 生产可用 给出一套可复制的最佳实践,并通过一个典型场景(告警日志检索)跑通关键链路。 1. 为什么是 Easysearch + Easy-Es # 1.1 Easysearch:面向企业的搜索与分析底座 # 企业落地搜索引擎时,通常要兼顾: 国产化替代:减少许可证风险与供应链风险 稳定与安全:资源保护、容灾、审计、安全能力 降本增效:冷热分离、快照、异步查询等能力组合 Easysearch 在兼容 Elasticsearch 的同时,更贴近企业生产环境的工程需求。 1.2 Easy-Es:把搜索“工程化”的 ORM 体验 # Easy-Es 的价值不是“把 ES 变成数据库”,而是把高频工作工程化: 类 MyBatis-Plus 的 Wrapper 条件构造(Lambda 类型安全) 自动索引托管:创建 / 更新 / 迁移(支持“平滑模式”) 高亮、聚合等能力的封装 支持通过 SQL 执行查询(适合作为轻量分析入口) 2." --- 在很多企业项目里,“搜索”早已不是锦上添花,而是支撑 **日志检索、告警排查、运营分析、内部统一搜索** 的核心底座。 但在工程实践中,团队往往会同时面临两类现实问题: - DSL 足够灵活,但**代码量大、可读性差、维护成本高** - 索引创建/变更、Mapping 升级、数据迁移……一堆“脏活累活”容易出错 - 国产化/信创落地时,希望替换底层引擎,同时又希望**尽量不改业务代码** 这也是 **Easysearch(分布式近实时搜索与分析引擎)** 与应用侧生态结合的价值所在:在保持兼容与工程效率的同时,提供更安全稳定、易维护的搜索底座。 而在应用侧,**Easy-Es for Easysearch(Easy-Es 2.1.0-easysearch)** 给了一个更工程化的答案: 用类似 MyBatis-Plus 的 ORM 方式,完成 Easysearch 的 CRUD、条件构造、高亮、聚合、SQL 查询,让业务研发“像写数据库代码一样写搜索”。 本文不会把重点放在“手把手搭项目”,而是围绕 **深度集成 + 生产可用** 给出一套可复制的最佳实践,并通过一个典型场景(告警日志检索)跑通关键链路。 --- ## 1. 为什么是 Easysearch + Easy-Es ### 1.1 Easysearch:面向企业的搜索与分析底座 企业落地搜索引擎时,通常要兼顾: - **国产化替代**:减少许可证风险与供应链风险 - **稳定与安全**:资源保护、容灾、审计、安全能力 - **降本增效**:冷热分离、快照、异步查询等能力组合 Easysearch 在兼容 Elasticsearch 的同时,更贴近企业生产环境的工程需求。 ### 1.2 Easy-Es:把搜索“工程化”的 ORM 体验 Easy-Es 的价值不是“把 ES 变成数据库”,而是把高频工作工程化: - 类 MyBatis-Plus 的 **Wrapper 条件构造**(Lambda 类型安全) - 自动索引托管:创建 / 更新 / 迁移(支持“平滑模式”) - 高亮、聚合等能力的封装 - 支持通过 SQL 执行查询(适合作为轻量分析入口) --- ## 2. 本文定位:适合谁?不适合谁? 在进入代码前,先明确本文适用范围。 ### 2.1 适合你,如果你正在做: - 从 Elasticsearch 平滑迁移到 Easysearch - 新建 Easysearch 业务索引,并希望减少 DSL 代码 - 想用更工程化方式管理索引、Mapping 演进、查询封装 - 需要高亮、聚合、SQL 等典型能力 ### 2.2 不适合你,如果你期待: - 完全不理解 term/match/agg 也能“无脑 ORM 化” - 用 SQL 替代所有 DSL 查询 - 用 Easy-Es 完成复杂 OLAP / 多表 JOIN / 任意分析 最健康的状态是:**80% 查询 ORM 化,20% 保留原生能力与工程边界。** --- ## 3. 整体架构:深度集成的请求链路 ![](/img/knowledge-base/best/easy-es-easysearch-orm-integration-best-practices/image-1.png) 图1:Spring Boot + Easy-Es + Easysearch 的请求链路 --- ## 4. 先读:深度集成的 6 条最佳实践原则 这 6 条原则决定了你是否能把 Easy-Es 用得“像工程”,而不是“像 Demo”。 ### 原则 1:索引是契约 索引字段类型、Analyzer、keyword/text 的选择,本质上是接口契约。 一旦上线,**尽量只增不改**。字段类型变更属于高风险操作,可能导致: - 写入失败 - 查询结果异常 - 必须全量重建数据 --- ### 原则 2:ORM 是降噪,不是屏蔽搜索引擎 Easy-Es 能降低 DSL 噪音,但团队仍应理解: - `eq` 对应 term 查询 - `match` 对应全文检索 - `groupBy` 对应 terms 聚合 - refresh/分页/聚合对性能的影响 否则很容易写出“看似 ORM,实则慢查询”的代码。 --- ### 原则 3:SQL 入口只能作为辅助通道 SQL 能力非常适合: - 轻量分析 - 运营临时查询 - 验证数据 但它不适合做: - 高并发业务接口 - 任意字段查询入口 - 未授权的“万能检索通道” SQL 入口必须做:鉴权 + 审计 + 参数白名单 + 限流。 --- ### 原则 4:刷新策略不要默认 immediate 很多团队为了“实时”,直接把 refresh-policy 设为 immediate。 这会显著增加写入成本,吞吐下降明显。 日志/告警通常可接受秒级可见,更温和的策略更合理。 --- ### 原则 5:深分页必须提前设计替代方案 `from + size` 深分页的代价会随着页数指数级变高。 需要深分页时,应优先考虑: - search_after(推荐) - scroll(按业务场景) --- ### 原则 6:索引托管模式要分环境选择 - 开发/迭代期:`smoothly` - 生产期:仍需评审、灰度、回滚预案 - 高风险字段变更:优先兼容设计,必要时新建索引重建 --- ## 5. 快速集成 版本基于:easy-es-boot-starter:2.1.0-easysearch Spring Boot 建议:2.7.x,JDK 8+ 因篇幅有限,下文仅介绍关键配置。 ### 5.1 Maven 依赖(最小集) ```xml org.dromara.easy-es easy-es-boot-starter 2.1.0-easysearch org.springframework.boot spring-boot-starter-web org.projectlombok lombok true ``` --- ### 5.2 application.yml:最小可用 + 生产建议 #### 最小可用配置(必选) ```yaml easy-es: enable: true address: localhost:9200 schema: http global-config: process-index-mode: smoothly print-dsl: false db-config: index-prefix: dev_ map-underscore-to-camel-case: true ``` #### 生产建议配置(可选但推荐) ```yaml easy-es: username: admin password: your_password_here keep-alive-millis: 18000 global-config: db-config: refresh-policy: wait_until field-strategy: not_empty enable-track-total-hits: true ``` 说明:refresh-policy 需要根据业务实时性选择,不建议默认 immediate。 --- ### 5.3 启动类:扫描 Mapper ```java @SpringBootApplication @EsMapperScan("com.example.demo.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` --- ## 6. 典型场景:告警日志检索 这个示例不是为了展示“所有 API”,而是模拟真实生产最常见的搜索场景: - 按 service + 时间范围 + keyword 检索 - 高亮展示命中内容 - 聚合统计(level/service) - (可选)SQL 给运营/分析使用 如果你只需要 CRUD 或简单查询,只看 6.1~6.2 即可。 --- ## 6.1 核心:实体类定义(索引映射 + 字段策略) 这是 ORM 深度集成的核心:字段类型、Analyzer、keyword/text 选择、高亮映射都在这里。 ```java @Data @Settings(shardsNum = 3, replicasNum = 1) @IndexName(value = "alarm_log", keepGlobalPrefix = true) public class AlarmLog { @IndexId(type = IdType.CUSTOMIZE) private String id; @IndexField(fieldType = FieldType.KEYWORD) private String service; @IndexField(fieldType = FieldType.KEYWORD) private String level; // INFO/WARN/ERROR @IndexField(fieldType = FieldType.DATE, dateFormat = "epoch_millis") private Long timestamp; @HighLight(mappingField = "highlightMessage", preTag = "", postTag = "") @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD, searchAnalyzer = Analyzer.IK_SMART) private String message; @IndexField(fieldType = FieldType.KEYWORD) private List tags; private String highlightMessage; } ``` #### 字段设计最佳实践(选择与原因) - `service/level/tags` 用 keyword:过滤与聚合稳定 - `message` 用 text:全文检索 + Analyzer - `timestamp` 用毫秒时间戳:范围查询与排序直观 - 高亮通过 `@HighLight` 驱动:业务代码无需额外配置 --- ## 6.2 Mapper:像 MyBatis 一样写接口 ```java public interface AlarmLogMapper extends BaseEsMapper { } ``` --- ## 6.3 查询:ORM 风格的 Wrapper(过滤 + 检索 + 排序 + 分页) ```java @Service @RequiredArgsConstructor public class AlarmLogService { private final AlarmLogMapper alarmLogMapper; public List search(String service, String keyword, Long start, Long end, int page, int size) { int safePage = Math.max(page, 1); int safeSize = Math.min(Math.max(size, 1), 500); int from = (safePage - 1) * safeSize; LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); if (StringUtils.hasText(service)) { wrapper.eq(AlarmLog::getService, service); } if (start != null) { wrapper.ge(AlarmLog::getTimestamp, start); } if (end != null) { wrapper.le(AlarmLog::getTimestamp, end); } if (StringUtils.hasText(keyword)) { wrapper.match(AlarmLog::getMessage, keyword); } wrapper.orderByDesc(AlarmLog::getTimestamp) .from(from) .size(safeSize); return alarmLogMapper.selectList(wrapper); } } ``` #### 查询侧最佳实践(关键点) - 过滤优先用 `eq/ge/le`(term/range) - 文本检索优先 `match`,短语用 `matchPhrase` - size 设置上限(例如 500),避免接口被滥用 - 深分页不要长期使用 `from + size`(见第 8 节) --- ## 6.4 高亮:让用户“看得见命中在哪里”(无需额外代码) Easy-Es 的高亮由实体字段上的 `@HighLight` 驱动。 你不需要额外写 `wrapper.highLight(...)` 之类的逻辑,只要正常 match 查询,返回结果里的 `highlightMessage` 会自动填充。 举例: - 搜索词:`超时` - message:`请求超时,已触发重试` - highlightMessage:`请求 超时,已触发重试` ![](/img/knowledge-base/best/easy-es-easysearch-orm-integration-best-practices/image-2.png) 图2:高亮效果示意 --- ## 6.5 聚合:按级别、按服务做分桶统计(告警质量分析必备) ```java @Service @RequiredArgsConstructor public class AlarmLogAggService { private final AlarmLogMapper alarmLogMapper; public Aggregations aggByLevelAndService(Long start, Long end) { LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); if (start != null) { wrapper.ge(AlarmLog::getTimestamp, start); } if (end != null) { wrapper.le(AlarmLog::getTimestamp, end); } wrapper.groupBy(false, AlarmLog::getLevel, AlarmLog::getService); SearchResponse response = alarmLogMapper.search(wrapper); return response.getAggregations(); } } ``` #### 聚合落地场景 - 日报/周报:按 level 分桶 - 服务健康度:按 service TopN - 告警质量:ERROR 占比、告警来源分布 --- ## 7. SQL 查询能力:适用场景 + 风险边界(可选但很实用) Easy-Es for Easysearch 提供 `executeSQL(String sql)` 入口,适合作为轻量分析与快速验证通道。 但必须强调: SQL 入口如果不收口,会非常容易变成“任意查询通道”,带来安全与性能风险。 ### 7.1 推荐的使用方式(示意) ```java @Service @RequiredArgsConstructor public class AlarmLogSqlService { private final AlarmLogMapper alarmLogMapper; private static final Set LEVEL_WHITELIST = Set.of("INFO", "WARN", "ERROR"); public String queryBySql(String level) { String sql = "SELECT * FROM dev_alarm_log LIMIT 10"; if (StringUtils.hasText(level)) { String upper = level.trim().toUpperCase(); if (!LEVEL_WHITELIST.contains(upper)) { throw new IllegalArgumentException("invalid level"); } sql = "SELECT * FROM dev_alarm_log WHERE level = '" + upper + "' LIMIT 10"; } return alarmLogMapper.executeSQL(sql); } } ``` ### 7.2 SQL 入口生产必做清单 - 必须鉴权(建议网关层 + 服务层双保险) - 必须审计(记录 SQL、用户、时间、耗时) - 参数必须白名单(不要放开任意字段) - 限制 LIMIT 上限、超时、频率 - 不要把 SQL 当成高并发业务接口 --- ## 8. 生产最佳实践 Checklist ### 8.1 索引命名与多环境隔离 - 用 `index-prefix` 区分环境:`dev_ / test_ / prod_` - 日志类索引建议按时间滚动(按天/月) - 配合生命周期管理:冷热分离、快照、归档 --- ### 8.2 自动索引托管:选对策略 - `smoothly` 适合开发/迭代期:减少手工迁移与停机 - 生产期 Mapping 变更仍需流程: - 评审 - 灰度 - 回滚预案 - 字段类型变更属于高风险操作:优先兼容设计 --- ### 8.3 写入与刷新策略:不要为了“实时”牺牲吞吐 - immediate:强实时,写入成本高 - wait_until:更温和,适合日志/告警 - 大吞吐写入建议配合: - 批量写 - 异步写入 - Kafka 削峰 --- ### 8.4 查询性能:避免深分页 - 常规分页:from + size 可用 - 深分页:优先 search_after - scroll 适合导出类任务(注意资源释放) --- ### 8.5 安全与合规 - 生产启用 https + security:账号、权限、审计 - SQL 入口与调试接口必须鉴权与限流 - 网关统一流量入口与防护(鉴权、限流、WAF) --- ## 9. 你将收获什么? 通过 Easy-Es 深度集成 Easysearch,你可以把“写搜索”从 DSL 工程体力活升级为: - **ORM 化**:CRUD / Query / HighLight / Agg / SQL 更像写数据库 - **工程化**:索引创建、更新、迁移由框架托管,减少人为失误 - **迁移友好**:底层引擎替换更平滑,应用改造更少 - **生产可控**:通过最佳实践与边界约束,避免把搜索当数据库用 --- ## 参考链接 - Easysearch 官网:[https://www.infinilabs.cn/products/easysearch/](https://www.infinilabs.cn/products/easysearch/) - Easy-Es项目仓库(Dromara):[https://gitee.com/dromara/easy-es](https://gitee.com/dromara/easy-es) 和 [https://github.com/dromara/easy-es](https://github.com/dromara/easy-es) - Easy-Es 2.1.0-easysearch 发布说明(含依赖与配置示例): [https://infinilabs.cn/blog/2025/easy-es-2.1.0-easysearch-release/](https://infinilabs.cn/blog/2025/easy-es-2.1.0-easysearch-release/) - Easysearch Java Client 文档: [https://docs.infinilabs.com/easysearch/main/docs/references/client/java-client/](https://docs.infinilabs.com/easysearch/main/docs/references/client/java-client/) ---