
芋道云 Elasticsearch 全文检索从配置到上线的完整实操【免费下载链接】yudao-cloudruoyi-vue-pro 全新 Cloud 版本优化重构所有功能。基于 Spring Cloud Alibaba MyBatis Plus Vue Element 实现的后台管理系统 用户小程序支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城、CRM、ERP、MES、IM、AI 大模型、IoT 物联网等功能。你的 ⭐️ Star ⭐️是作者生发的动力项目地址: https://gitcode.com/gh_mirrors/yu/yudao-cloud用户在商品列表里输入无绳耳机商品库里的却叫无线立体声耳机——LIKE 查询直接返回空用户只能换个词再碰运气。这篇文章以商城商品模块为例演示在芋道云yudao-cloud中接入 ElasticsearchES全文检索的完整链路配连接、建索引、同步数据、带高亮的搜索接口最后给一份可直接照做的生产化清单。先看清 LIKE 和 ES 的差异选 ES 前先确认数据库检索到底差在哪。一张表说清楚对比维度数据库 LIKEES 检索分词不支持靠人肉拆词ik 等分词器自动切分容错必须逐字匹配支持模糊度、拼写纠错排序只能按时间/销量按相关性打分可叠加业务排序数据量级大表%xx%全表扫描倒排索引千万级仍在百毫秒内结论只有一个用户量上去以后搜索框必须交给专门的检索引擎而不是让主库硬扛。配依赖、连接、索引、同步一条编号流跑通框架目录yudao-framework/目前没有 ES 专属 starter直接引入官方 Spring Boot starter 即可。下面四步按顺序做每步一段核心代码。1. 加依赖依赖版本由 yudao-dependencies/pom.xml 统一管理Spring Boot 2.7 会锁定 Spring Data Elasticsearch 5.x 与 ES 7.17无需手动写版本号。在yudao-module-mall/yudao-module-product-server/pom.xml里加一行dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-elasticsearch/artifactId /dependency引入后容器里会自动装配ElasticsearchRestTemplate后续直接注入即可。2. 配好 ES 连接商品服务的 application.yaml 按环境追加连接段地址、账号按你的部署改spring: elasticsearch: uris: http://localhost:9200 username: elastic password: changeme connection-timeout: 5000 socket-timeout: 3000两个 timeout 建议压短一点ES 抖动时业务请求能快速失败而不是挂着拖慢线程池。3. 定义索引实体与 Repository字段与商品 SPU 表ProductSpuDO对齐即可price沿用项目的分为单位检索结果里再换算成元Data Document(indexName product_spu) public class ProductSpuDocument { Id private Long id; Field(type FieldType.Text, analyzer ik_max_word) private String name; Field(type FieldType.Text, analyzer ik_max_word) private String keyword; Field(type FieldType.Text, analyzer ik_max_word) private String description; Field(type FieldType.Keyword) private String categoryId; Field(type FieldType.Long) private Long price; // 单位分 Field(type FieldType.Date) private LocalDateTime createTime; }注意analyzer ik_max_word要求 ES 侧先装好 IK 分词插件没装就退化成standard中文按单字切分也能用只是精度差一截。再让 Spring 帮你管理增删改查public interface ProductSpuDocumentRepository extends ElasticsearchRepositoryProductSpuDocument, Long { }4. 把库里的数据灌进索引索引是数据库的读副本所以写一份同步服务单条同步挂到商品的创建、更新事件上全量同步留给首次建索引或索引损坏后重建。DO 转 Document 用框架自带的BeanUtils一行搞定Service RequiredArgsConstructor public class ProductSpuSearchService { private final ProductSpuDocumentRepository repository; private final ProductSpuMapper spuMapper; public void sync(Long id) { ProductSpuDO spu spuMapper.selectById(id); if (spu ! null) { repository.save(BeanUtils.toBean(spu, ProductSpuDocument.class)); } } public void syncAll() { repository.deleteAll(); // 重建前先清空 ListProductSpuDO list spuMapper.selectList(); repository.saveAll(BeanUtils.toBean(list, ProductSpuDocument.class)); } }改商品走sync保证实时syncAll只在初始化场景跑一次两条路径别混在一起。一个能直接用的商品搜索接口查询构建、高亮、出参三段各管一件事。先写查询多字段命中 自动模糊度高亮用em包裹命中词分页交给PageRequestpublic PageProductSpuDocument search(String keyword, Integer pageNo, Integer pageSize) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword, name, keyword, description) .fuzziness(Fuzziness.AUTO)) .withHighlightFields( new HighlightBuilder.Field(name).preTags(em).postTags(/em), new HighlightBuilder.Field(description).preTags(em).postTags(/em)) .withPageable(PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, createTime))) .build(); SearchHitsProductSpuDocument hits elasticsearchRestTemplate.search(query, ProductSpuDocument.class); ListProductSpuDocument list new ArrayList(); for (SearchHitProductSpuDocument hit : hits.getSearchHits()) { ProductSpuDocument doc hit.getContent(); ListString names hit.getHighlightField(name); if (!names.isEmpty()) { doc.setName(names.get(0)); } ListString desc hit.getHighlightField(description); if (!desc.isEmpty()) { doc.setDescription(desc.get(0)); } list.add(doc); } return new PageImpl(list, PageRequest.of(pageNo - 1, pageSize), hits.getTotalHits()); }高亮串是前端直接渲染的命中词前后各包一个em前端样式把em标成主题色即可。控制器保持项目统一的CommonResult出参GetMapping(/search) Operation(summary 商品全文检索) public CommonResultPageProductSpuDocument search( RequestParam(keyword) String keyword, RequestParam(name pageNo, defaultValue 1) Integer pageNo, RequestParam(name pageSize, defaultValue 10) Integer pageSize) { return CommonResult.success(searchService.search(keyword, pageNo, pageSize)); }到这里从依赖到接口是一条不断线的链路接口文档里直接可测。进阶场景搜索建议 与 组合过滤排序⚡️ 搜索建议用户还没输完词候选词先弹出来。给索引加一个completion类型的字段如nameSuggest同步时把商品名灌进去建议查询只查这一个字段public ListString suggest(String prefix) { CompletionSuggestionBuilder sb SuggestBuilders.completionSuggestion(nameSuggest) .prefix(prefix).size(10); NativeSearchQuery query new NativeSearchQueryBuilder() .withSourceBuilder(new SearchSourceBuilder() .suggest(new SuggestBuilder().addSuggestion(spu, sb))) .build(); SearchHitsProductSpuDocument hits elasticsearchRestTemplate.search(query, ProductSpuDocument.class); return hits.getSuggest().getSuggestion(spu).getOptions().stream() .map(CompletionSuggestion.Entry.Option::getText) .map(Text::string) .collect(Collectors.toList()); }size是每次回传的候选数按前端下拉框高度调10~20 之间最常用。 多条件过滤 排序关键词只是召回的一半价格区间、分类才是真正的筛选条件。过滤走filter子句不走相关性打分还能被缓存排序支持前端传字段BoolQueryBuilder bool QueryBuilders.boolQuery() .must(QueryBuilders.multiMatchQuery(keyword, name, keyword, description)); if (minPrice ! null) { bool.filter(QueryBuilders.rangeQuery(price).gte(minPrice)); } if (maxPrice ! null) { bool.filter(QueryBuilders.rangeQuery(price).lte(maxPrice)); } if (categoryId ! null) { bool.filter(QueryBuilders.termQuery(categoryId, categoryId)); } NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(bool) .withPageable(PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, sortField))) // 如 createTime / price .build();sortField建议白名单校验后再拼进Sort防止前端把内部字段名塞进来。上生产前照这份清单过一遍架构图里 Elasticsearch 挂在存储服务一侧配套监控同样要跟上。批量写全量重建走 Bulk 批量写入别在循环里逐条save网络往返少一个数量级。异步更新商品落库后发一条 MQ 消息再更新索引参考 yudao-spring-boot-starter-mq 的《芋道 Spring Boot 消息队列 RabbitMQ 入门》写库和写索引解耦索引侧失败可重投。分片副本单索引 5GB 以内给 1 个主分片即可读多写少就加 1 个副本提吞吐别凭感觉拉高。热点缓存首页热词的搜索结果放进 Redis配置参考 yudao-spring-boot-starter-redis 的《芋道 Spring Boot Redis 入门》分钟级过期足够。健康与指标打开 Spring Boot Actuator 的 health 端点ES 连接状态、索引大小、慢查询都会进来接入方式见 yudao-spring-boot-starter-monitor 的《芋道 Spring Boot 监控端点 Actuator 入门》。重建预案给syncAll()留一个管理端按钮或定时任务索引结构变更、分词器升级后一键重建不用手写脚本。收尾从一条依赖、一份 yaml到高亮搜索、建议词、过滤排序和上线清单这条链路走完后商品搜索框才算真正能干活。如果还想把全文检索往对话式检索方向再推一步yudao-module-ai/ 里的大模型集成源码值得一读。觉得有用的话顺手给 yudao-cloud 点个 Star你的支持是作者持续迭代的动力。【免费下载链接】yudao-cloudruoyi-vue-pro 全新 Cloud 版本优化重构所有功能。基于 Spring Cloud Alibaba MyBatis Plus Vue Element 实现的后台管理系统 用户小程序支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城、CRM、ERP、MES、IM、AI 大模型、IoT 物联网等功能。你的 ⭐️ Star ⭐️是作者生发的动力项目地址: https://gitcode.com/gh_mirrors/yu/yudao-cloud创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考