当前位置: 首页 > 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.jsqmd.com/news/503399/

相关文章:

  • MQTT实战:用Mosquitto和libmosquitto在Ubuntu上搭建物联网消息系统(附C代码示例)
  • 探索Mini Kossel:如何用开源硬件构建你的第一台三角洲3D打印机
  • UniMol实战:手把手教你用3D Transformer生成分子构象(附代码解析)
  • RAG大模型“解幻觉“神器?从原理到实战,带你秒懂知识增强生成技术!
  • MediaCreationTool1909使用全攻略:从下载到安装Win10的完整流程
  • IPv4与IPv6深度解析:从地址枯竭到下一代网络的演进
  • Phi-3-Mini-128K多轮对话效果展示:复杂技术问题拆解与解答
  • CMake 策略 CMP0077:子目录中 option() 与父目录同名变量的行为及规避方法
  • 基于 antv x6 构建智能客服对话流程图:从零实现到生产级优化
  • Verilog函数vs任务:从数码管驱动设计看两者的核心差异与选用原则
  • AI建站避坑指南:10个你最关心的问题与解决方案
  • AIAgent智能体 Dify
  • Spring Boot 整合 Redis
  • Kubernetes VIP 分配与负载均衡技术研究
  • 手把手教你Ubuntu20.04安装ROS2:从零开始搭建机器人开发环境
  • 广州半封闭复读学校深度解析及10家优质机构推荐 - 妙妙水侠
  • 彻底吃透 Java OOM 异常:从原理、场景、排查到解决方案全攻略
  • 分人群解决方案:哪类AI建站工具适合你?
  • Claude Architect认证到底考什么?一个重度用户用半年实战逐项拆解
  • web后端----后端框架基本架构、基本流程
  • 突破音乐格式枷锁:4大维度重构NCM文件的自由转换技术
  • 3大突破!Avalonia让跨平台音频界面开发效率提升200%
  • Ubuntu 22.04 LTS下NVIDIA驱动安装避坑指南:如何用终端一键搞定(附常见错误解决)
  • Step-by-Step Guide to Installing Anolis OS 8.10 for Cloud Environments
  • Qwen3智能字幕对齐系统在在线教育场景的应用
  • X上100万浏览只赚40美元,YouTube却能赚8000美元?程序员副业出海的200倍身份跃迁指南
  • 05_Priority Queues 优先队列
  • 彻底搞懂 Java 垃圾回收(GC)
  • OpenCV实战:5分钟搞定图像模板匹配(NCC算法+C++代码详解)
  • 6.4 日志到底怎么写才有用?排障效率提升的底层方法