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

Spring Boot 整合 Elasticsearch指南

Elasticsearch是一个基于Lucene的分布式搜索和分析引擎,常用于全文检索、日志分析、实时数据分析等场景。本文详细介绍Spring Boot如何整合Elasticsearch,实现高效的搜索功能。

一、Elasticsearch简介

Elasticsearch(简称ES)是一个开源的分布式搜索引擎,具有以下特点:

  • 分布式架构,支持海量数据

  • 全文检索能力强大

  • 实时性好

  • 支持RESTful API

  • 配套Kibana提供可视化界面

基本概念:

  • Index(索引):相当于数据库

  • Type(类型):相当于表(ES7已移除)

  • Document(文档):相当于行

  • Field(字段):相当于列

  • Shard(分片):数据分片

  • Replica(副本)::数据副本

二、引入依赖

<!--Elasticsearch--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>

三、配置

spring: elasticsearch: uris:http://localhost:9200 # ES地址,多个用逗号分隔 # 账号密码(如果需要) # username: elastic # password: password

四、创建实体类

@Data // 指定索引名称和类型 @Document(indexName = "user", shards = 3, replicas = 1) publicclassUserDocument{ // 主键 @Id private String id; // 姓名 - 字段类型为keyword(不分词) @Field(type = FieldType.Keyword) private String name; // 邮箱 - keyword类型 @Field(type = FieldType.Keyword) private String email; // 年龄 - integer类型 @Field(type = FieldType.Integer) private Integer age; // 简介 - text类型(会分词) @Field(type = FieldType.Text, analyzer = "ik_max_word") private String bio; // 创建时间 @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis) private LocalDateTime createTime; }

常用FieldType:

  • Text:文本类型,会分词,用于全文检索

  • Keyword:关键字类型,不分词,用于精确匹配

  • Integer/Long:整数类型

  • Float/Double:浮点类型

  • Boolean:布尔类型

  • Date:日期类型

  • Object:对象类型

  • Nested:嵌套对象类型

五、创建Repository

publicinterfaceUserSearchRepositoryextendsElasticsearchRepository<UserDocument, String> { // 根据姓名查询 List<UserDocument> findByName(String name); // 根据年龄范围查询 List<UserDocument> findByAgeBetween(Integer from, Integer to); // 模糊查询 List<UserDocument> findByBioContaining(String keyword); }

六、基本操作

@Service publicclassUserSearchService{ @Autowired private UserSearchRepository userSearchRepository; /** * 保存文档 */ publicvoidsave(UserDocument user){ userSearchRepository.save(user); } /** * 批量保存 */ publicvoidbatchSave(List<UserDocument> users){ userSearchRepository.saveAll(users); } /** * 根据ID查询 */ public UserDocument findById(String id){ return userSearchRepository.findById(id).orElse(null); } /** * 查询所有 */ public Iterable<UserDocument> findAll(){ return userSearchRepository.findAll(); } /** * 根据姓名查询 */ public List<UserDocument> findByName(String name){ return userSearchRepository.findByName(name); } /** * 删除 */ publicvoiddelete(String id){ userSearchRepository.deleteById(id); } /** * 删除所有 */ publicvoiddeleteAll(){ userSearchRepository.deleteAll(); } }

七、复杂查询(NativeSearchQuery)

有时候复杂的查询逻辑无法通过方法名来实现,需要使用NativeSearchQuery。

@Service publicclassUserSearchService{ @Autowired private ElasticsearchTemplate elasticsearchTemplate; /** * 条件查询 */ public List<UserDocument> searchByCondition(String keyword, Integer age){ // 构建查询条件 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 姓名或简介中包含关键词 if (StringUtils.isNotBlank(keyword)) { boolQueryBuilder.should(QueryBuilders.matchQuery("name", keyword)); boolQueryBuilder.should(QueryBuilders.matchQuery("bio", keyword)); } // 年龄大于指定值 if (age != null) { boolQueryBuilder.must(QueryBuilders.rangeQuery("age").gte(age)); } // 构建查询对象 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(boolQueryBuilder) .build(); // 执行查询 SearchHits<UserDocument> hits = elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为列表 return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); } /** * 分页查询 */ public Page<UserDocument> searchPage(String keyword, int page, int size){ // 查询条件:匹配关键词 MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("bio", keyword); // 分页查询 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(queryBuilder) .withPageable(PageRequest.of(page, size)) .build(); SearchHits<UserDocument> hits = elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为分页结果 List<UserDocument> list = hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); returnnew PageImpl<>(list, PageRequest.of(page, size), hits.getTotalHits()); } /** * 聚合查询 - 按年龄分组统计 */ public Map<Integer, Long> aggregateByAge(){ // 构建聚合 TermsAggregationBuilder aggregation = AggregationBuilders .terms("age_agg") .field("age") .size(100); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withAggregation(aggregation) .build(); SearchHits<UserDocument> hits = elasticsearchTemplate.search(searchQuery, UserDocument.class); // 解析聚合结果 Terms terms = hits.getAggregations().get("age_agg"); Map<Integer, Long> result = new HashMap<>(); for (Terms.Bucket bucket : terms.getBuckets()) { result.put(bucket.getKeyAsNumber().intValue(), bucket.getDocCount()); } return result; } /** * 高亮显示 */ public List<UserDocument> searchWithHighlight(String keyword){ // 构建高亮字段 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("bio"); // 对bio字段高亮 highlightBuilder.preTags("<span style='color:red'>"); // 前缀 highlightBuilder.postTags("</span>"); // 后缀 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("bio", keyword)) .withHighlightBuilder(highlightBuilder) .build(); SearchHits<UserDocument> hits = elasticsearchTemplate.search(searchQuery, UserDocument.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); } }

八、ES与MySQL数据同步

实际项目中,ES的数据通常来自MySQL,需要保持数据同步。

方案一:Logstash同步

  • 通过Canal监听MySQL binlog

  • 写入ES(推荐生产环境使用)

方案二:定时任务同步

@Service publicclassDataSyncService{ @Autowired private UserMapper userMapper; @Autowired private UserSearchRepository userSearchRepository; /** * 全量同步 - 定时执行 */ @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点 publicvoidfullSync(){ // 查询所有用户 List<User> users = userMapper.selectList(null); // 转换为ES文档 List<UserDocument> documents = users.stream() .map(this::convertToDocument) .collect(Collectors.toList()); // 批量保存到ES userSearchRepository.saveAll(documents); System.out.println("全量同步完成,共同步 " + documents.size() + " 条数据"); } /** * 增量同步 - 通过消息队列 */ publicvoidincrementalSync(User user){ UserDocument document = convertToDocument(user); userSearchRepository.save(document); } private UserDocument convertToDocument(User user){ UserDocument doc = new UserDocument(); doc.setId(user.getId().toString()); doc.setName(user.getName()); doc.setEmail(user.getEmail()); doc.setAge(user.getAge()); doc.setBio(user.getBio()); doc.setCreateTime(user.getCreateTime()); return doc; } }

九、总结

Elasticsearch是处理海量数据搜索的利器,本文介绍了:

  • 基本概念:Index、Document、Field

  • Spring Data Elasticsearch:简化ES操作

  • Repository:快速实现CRUD

  • NativeSearchQuery:复杂查询

  • 分页、聚合、高亮:高级功能

  • 数据同步:ES与MySQL同步方案

记住:ES不是替代MySQL,而是MySQL的补充!

http://www.cnnetsun.cn/news/1371756.html

相关文章:

  • AI测试生成革命:Cover-Agent如何重塑软件质量保障
  • 高效运维新选择:Shell 脚本自动化生成可视化 Linux 系统健康报告
  • AI建站工具怎么选?从零到一搭建网站的完整流程指南
  • 突破音乐格式枷锁:4大维度重构NCM文件的自由转换技术
  • Python 实战2:新浪新闻静态 + 动态数据采集与清洗全流程
  • Element Plus 2.2.27 的单选框 Radio 组件,选中一个选项后,全部选项都变为选中状态
  • U8g2自定义中文字库实战:从零构建Arduino OLED专属字体
  • YOLOv5后处理全流程拆解:从6万个候选框到最终结果的‘过滤漏斗’
  • Appwrite:开源后端即服务神器,快速构建Web和移动应用
  • nodejs+vue基于springboot的安全生产培训管理系统
  • 【异常】OpenClaw LLM请求超时故障全链路排查与解决方案 LLM request timed out. 网络连接超时了
  • Ubuntu 22.04换国内源保姆级教程(附清华/阿里云/中科大源对比)
  • 股票预测避坑指南:为什么你的Transformer模型跑不过LSTM?(含数据集+超参配置)
  • C++游戏毕设从零起步:新手避坑指南与最小可运行架构实践
  • SpringBoot微服务性能调优实战:SkyWalking链路追踪深度集成指南
  • 【优选算法篇】快速排序模型——从数组划分到快速选择
  • 【数据结构与算法】 二叉树做题
  • YOLOv11模型调参指南:如何让交通灯检测准确率提升15%(附训练曲线分析)
  • 知识图谱在教育领域的5个创新应用:从个性化推荐到自适应学习(含Django实现案例)
  • 3.5%稳增!全球电子引信2032年锚定12.41亿美元
  • 2026 年 8 款安卓数据擦除软件和应用对比
  • ESP32玩转SSD1306 OLED:Adafruit_GFX与u8g2库实战对比(附避坑指南)
  • 3大向量索引终极指南:如何在Milvus中选择最适合你的AI应用方案
  • 如何为Steam打造专属交互体验:SFP工具的深度探索
  • TDengine连接池配置实战:HikariCP与Java应用的高效集成指南
  • 三边封制袋机程序(采用松下PLC及威纶通触摸屏控制,前后双伺服送料,高效温控模块常州汇邦
  • 告别重复劳动:用快马AI自动化你的Python数据分析周报任务
  • Mirage Flow 科学计算应用:与MATLAB协同进行数据分析
  • 【Frida Android】实战篇:Java层Hook进阶——拦截与篡改普通方法参数
  • 基于快马平台提升java八股文复习与知识整理效率