Elasticsearch是一个基于Lucene的分布式搜索和分析引擎常用于全文检索、日志分析、实时数据分析等场景。本文详细介绍Spring Boot如何整合Elasticsearch实现高效的搜索功能。一、Elasticsearch简介Elasticsearch简称ES是一个开源的分布式搜索引擎具有以下特点分布式架构支持海量数据全文检索能力强大实时性好支持RESTful API配套Kibana提供可视化界面基本概念Index索引相当于数据库Type类型相当于表ES7已移除Document文档相当于行Field字段相当于列Shard分片数据分片Replica副本数据副本二、引入依赖!--Elasticsearch-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-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; }常用FieldTypeText文本类型会分词用于全文检索Keyword关键字类型不分词用于精确匹配Integer/Long整数类型Float/Double浮点类型Boolean布尔类型Date日期类型Object对象类型Nested嵌套对象类型五、创建RepositorypublicinterfaceUserSearchRepositoryextendsElasticsearchRepositoryUserDocument, String { // 根据姓名查询 ListUserDocument findByName(String name); // 根据年龄范围查询 ListUserDocument findByAgeBetween(Integer from, Integer to); // 模糊查询 ListUserDocument findByBioContaining(String keyword); }六、基本操作Service publicclassUserSearchService{ Autowired private UserSearchRepository userSearchRepository; /** * 保存文档 */ publicvoidsave(UserDocument user){ userSearchRepository.save(user); } /** * 批量保存 */ publicvoidbatchSave(ListUserDocument users){ userSearchRepository.saveAll(users); } /** * 根据ID查询 */ public UserDocument findById(String id){ return userSearchRepository.findById(id).orElse(null); } /** * 查询所有 */ public IterableUserDocument findAll(){ return userSearchRepository.findAll(); } /** * 根据姓名查询 */ public ListUserDocument 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 ListUserDocument 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(); // 执行查询 SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为列表 return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); } /** * 分页查询 */ public PageUserDocument 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(); SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为分页结果 ListUserDocument list hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); returnnew PageImpl(list, PageRequest.of(page, size), hits.getTotalHits()); } /** * 聚合查询 - 按年龄分组统计 */ public MapInteger, Long aggregateByAge(){ // 构建聚合 TermsAggregationBuilder aggregation AggregationBuilders .terms(age_agg) .field(age) .size(100); NativeSearchQuery searchQuery new NativeSearchQueryBuilder() .withAggregation(aggregation) .build(); SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 解析聚合结果 Terms terms hits.getAggregations().get(age_agg); MapInteger, Long result new HashMap(); for (Terms.Bucket bucket : terms.getBuckets()) { result.put(bucket.getKeyAsNumber().intValue(), bucket.getDocCount()); } return result; } /** * 高亮显示 */ public ListUserDocument searchWithHighlight(String keyword){ // 构建高亮字段 HighlightBuilder highlightBuilder new HighlightBuilder(); highlightBuilder.field(bio); // 对bio字段高亮 highlightBuilder.preTags(span stylecolor:red); // 前缀 highlightBuilder.postTags(/span); // 后缀 NativeSearchQuery searchQuery new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery(bio, keyword)) .withHighlightBuilder(highlightBuilder) .build(); SearchHitsUserDocument 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(){ // 查询所有用户 ListUser users userMapper.selectList(null); // 转换为ES文档 ListUserDocument 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、FieldSpring Data Elasticsearch简化ES操作Repository快速实现CRUDNativeSearchQuery复杂查询分页、聚合、高亮高级功能数据同步ES与MySQL同步方案记住ES不是替代MySQL而是MySQL的补充
Spring Boot 整合 Elasticsearch指南
Elasticsearch是一个基于Lucene的分布式搜索和分析引擎常用于全文检索、日志分析、实时数据分析等场景。本文详细介绍Spring Boot如何整合Elasticsearch实现高效的搜索功能。一、Elasticsearch简介Elasticsearch简称ES是一个开源的分布式搜索引擎具有以下特点分布式架构支持海量数据全文检索能力强大实时性好支持RESTful API配套Kibana提供可视化界面基本概念Index索引相当于数据库Type类型相当于表ES7已移除Document文档相当于行Field字段相当于列Shard分片数据分片Replica副本数据副本二、引入依赖!--Elasticsearch-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-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; }常用FieldTypeText文本类型会分词用于全文检索Keyword关键字类型不分词用于精确匹配Integer/Long整数类型Float/Double浮点类型Boolean布尔类型Date日期类型Object对象类型Nested嵌套对象类型五、创建RepositorypublicinterfaceUserSearchRepositoryextendsElasticsearchRepositoryUserDocument, String { // 根据姓名查询 ListUserDocument findByName(String name); // 根据年龄范围查询 ListUserDocument findByAgeBetween(Integer from, Integer to); // 模糊查询 ListUserDocument findByBioContaining(String keyword); }六、基本操作Service publicclassUserSearchService{ Autowired private UserSearchRepository userSearchRepository; /** * 保存文档 */ publicvoidsave(UserDocument user){ userSearchRepository.save(user); } /** * 批量保存 */ publicvoidbatchSave(ListUserDocument users){ userSearchRepository.saveAll(users); } /** * 根据ID查询 */ public UserDocument findById(String id){ return userSearchRepository.findById(id).orElse(null); } /** * 查询所有 */ public IterableUserDocument findAll(){ return userSearchRepository.findAll(); } /** * 根据姓名查询 */ public ListUserDocument 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 ListUserDocument 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(); // 执行查询 SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为列表 return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); } /** * 分页查询 */ public PageUserDocument 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(); SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 转换为分页结果 ListUserDocument list hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); returnnew PageImpl(list, PageRequest.of(page, size), hits.getTotalHits()); } /** * 聚合查询 - 按年龄分组统计 */ public MapInteger, Long aggregateByAge(){ // 构建聚合 TermsAggregationBuilder aggregation AggregationBuilders .terms(age_agg) .field(age) .size(100); NativeSearchQuery searchQuery new NativeSearchQueryBuilder() .withAggregation(aggregation) .build(); SearchHitsUserDocument hits elasticsearchTemplate.search(searchQuery, UserDocument.class); // 解析聚合结果 Terms terms hits.getAggregations().get(age_agg); MapInteger, Long result new HashMap(); for (Terms.Bucket bucket : terms.getBuckets()) { result.put(bucket.getKeyAsNumber().intValue(), bucket.getDocCount()); } return result; } /** * 高亮显示 */ public ListUserDocument searchWithHighlight(String keyword){ // 构建高亮字段 HighlightBuilder highlightBuilder new HighlightBuilder(); highlightBuilder.field(bio); // 对bio字段高亮 highlightBuilder.preTags(span stylecolor:red); // 前缀 highlightBuilder.postTags(/span); // 后缀 NativeSearchQuery searchQuery new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery(bio, keyword)) .withHighlightBuilder(highlightBuilder) .build(); SearchHitsUserDocument 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(){ // 查询所有用户 ListUser users userMapper.selectList(null); // 转换为ES文档 ListUserDocument 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、FieldSpring Data Elasticsearch简化ES操作Repository快速实现CRUDNativeSearchQuery复杂查询分页、聚合、高亮高级功能数据同步ES与MySQL同步方案记住ES不是替代MySQL而是MySQL的补充