Spring Boot 与 Elasticsearch 8.x 集成实战从入门到精通一、引言在现代应用开发中全文搜索和数据分析已经成为不可或缺的功能。Elasticsearch 作为一款强大的开源搜索和分析引擎凭借其分布式架构、实时搜索能力和强大的聚合分析功能已经成为业界首选的搜索解决方案。Spring Boot 提供了对 Elasticsearch 的原生支持通过 Spring Data Elasticsearch 模块开发者可以轻松地将 Elasticsearch 集成到 Spring Boot 应用中。本文将深入探讨 Spring Boot 与 Elasticsearch 8.x 的集成实践包括环境配置、核心 API 使用、高级查询以及性能优化等方面。二、环境准备与依赖配置2.1 依赖引入在pom.xml中添加 Spring Data Elasticsearch 依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-elasticsearch/artifactId /dependency2.2 配置文件设置在application.yml中配置 Elasticsearch 连接信息spring: elasticsearch: uris: http://localhost:9200 username: elastic password: changeme connection-timeout: 10s socket-timeout: 30s2.3 客户端配置类创建自定义的 Elasticsearch 客户端配置Configuration public class ElasticsearchConfig { Value(${spring.elasticsearch.uris}) private String elasticsearchUris; Value(${spring.elasticsearch.username}) private String username; Value(${spring.elasticsearch.password}) private String password; Bean public RestClient restClient() { final CredentialsProvider credentialsProvider new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password) ); return RestClient.builder( HttpHost.create(elasticsearchUris) ).setHttpClientConfigCallback(httpClientBuilder - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) ).build(); } Bean public ElasticsearchOperations elasticsearchOperations( RestClient restClient ) { ElasticsearchRestTemplate template new ElasticsearchRestTemplate(restClient); return template; } }三、索引管理与文档操作3.1 文档实体定义定义一个商品实体类Document(indexName products, createIndex false) Setting(shards 3, replicas 2) public class Product { Id private String id; Field(type FieldType.Text, analyzer ik_max_word) private String name; Field(type FieldType.Keyword) private String category; Field(type FieldType.Double) private Double price; Field(type FieldType.Integer) private Integer stock; Field(type FieldType.Date, format DateFormat.date_time) private LocalDateTime createTime; Field(type FieldType.Nested) private ListReview reviews; // Getters and Setters } public class Review { Field(type FieldType.Text) private String content; Field(type FieldType.Integer) private Integer rating; Field(type FieldType.Date) private LocalDateTime reviewTime; // Getters and Setters }3.2 Repository 接口定义创建数据访问层接口public interface ProductRepository extends ElasticsearchRepositoryProduct, String { ListProduct findByNameContaining(String name); ListProduct findByCategory(String category); ListProduct findByPriceBetween(Double minPrice, Double maxPrice); Query({\bool\: {\must\: [{\match\: {\name\: \?0\}}]}}) ListProduct searchByName(String name); }3.3 基本 CRUD 操作Service public class ProductService { private final ProductRepository productRepository; private final ElasticsearchOperations elasticsearchOperations; public ProductService(ProductRepository productRepository, ElasticsearchOperations elasticsearchOperations) { this.productRepository productRepository; this.elasticsearchOperations elasticsearchOperations; } // 创建文档 public Product save(Product product) { return productRepository.save(product); } // 批量创建 public ListProduct saveAll(ListProduct products) { return productRepository.saveAll(products); } // 根据ID查询 public OptionalProduct findById(String id) { return productRepository.findById(id); } // 查询所有 public ListProduct findAll() { ListProduct result new ArrayList(); productRepository.findAll().forEach(result::add); return result; } // 根据ID删除 public void deleteById(String id) { productRepository.deleteById(id); } // 删除所有 public void deleteAll() { productRepository.deleteAll(); } }四、高级查询与聚合分析4.1 全文搜索public ListProduct searchProducts(String keyword) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword, name, category)) .withHighlightFields(new HighlightBuilder.Field(name)) .withHighlightBuilder(new HighlightBuilder() .preTags(em) .postTags(/em)) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }4.2 布尔查询组合public ListProduct advancedSearch(String keyword, String category, Double minPrice, Double maxPrice) { BoolQueryBuilder boolQuery QueryBuilders.boolQuery(); if (keyword ! null !keyword.isEmpty()) { boolQuery.must(QueryBuilders.matchQuery(name, keyword)); } if (category ! null !category.isEmpty()) { boolQuery.filter(QueryBuilders.termQuery(category, category)); } if (minPrice ! null || maxPrice ! null) { RangeQueryBuilder rangeQuery QueryBuilders.rangeQuery(price); if (minPrice ! null) { rangeQuery.gte(minPrice); } if (maxPrice ! null) { rangeQuery.lte(maxPrice); } boolQuery.filter(rangeQuery); } NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(boolQuery) .withSort(SortBuilders.fieldSort(price).order(SortOrder.ASC)) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }4.3 聚合分析public MapString, Long getCategoryStatistics() { NativeSearchQuery query new NativeSearchQueryBuilder() .addAggregation(AggregationBuilders.terms(category_stats).field(category)) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); Terms categoryStats hits.getAggregations().get(category_stats); return categoryStats.getBuckets().stream() .collect(Collectors.toMap( bucket - bucket.getKeyAsString(), bucket - bucket.getDocCount() )); } public MapString, Double getPriceStatistics() { NativeSearchQuery query new NativeSearchQueryBuilder() .addAggregation(AggregationBuilders.stats(price_stats).field(price)) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); Stats priceStats hits.getAggregations().get(price_stats); MapString, Double stats new HashMap(); stats.put(min, priceStats.getMin()); stats.put(max, priceStats.getMax()); stats.put(avg, priceStats.getAvg()); stats.put(sum, priceStats.getSum()); return stats; }4.4 嵌套文档查询public ListProduct searchByReviewContent(String reviewContent) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.nestedQuery( reviews, QueryBuilders.matchQuery(reviews.content, reviewContent), ScoreMode.Avg )) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }五、分页与排序5.1 分页查询public PageProduct searchWithPagination(String keyword, int page, int size) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery(name, keyword)) .withPageable(PageRequest.of(page, size)) .build(); SearchPageProduct searchPage elasticsearchOperations.searchForPage(query, Product.class); return searchPage; }5.2 多字段排序public ListProduct searchWithMultiSort(String keyword) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery(name, keyword)) .withSort(SortBuilders.fieldSort(price).order(SortOrder.DESC)) .withSort(SortBuilders.fieldSort(createTime).order(SortOrder.DESC)) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }六、索引管理6.1 创建索引public void createIndex(String indexName) { CreateIndexRequest request new CreateIndexRequest(indexName); MapString, Object settings new HashMap(); settings.put(number_of_shards, 3); settings.put(number_of_replicas, 2); request.settings(settings); MapString, Object mapping new HashMap(); MapString, Object properties new HashMap(); MapString, Object name new HashMap(); name.put(type, text); name.put(analyzer, ik_max_word); properties.put(name, name); MapString, Object category new HashMap(); category.put(type, keyword); properties.put(category, category); MapString, Object price new HashMap(); price.put(type, double); properties.put(price, price); mapping.put(properties, properties); request.mapping(mapping); restClient().indices().create(request, RequestOptions.DEFAULT); }6.2 删除索引public void deleteIndex(String indexName) { DeleteIndexRequest request new DeleteIndexRequest(indexName); restClient().indices().delete(request, RequestOptions.DEFAULT); }6.3 索引别名管理public void addAlias(String indexName, String aliasName) { AliasActions aliasAction new AliasActions() .add(AliasAction.add().index(indexName).alias(aliasName)); restClient().indices().updateAliases(new UpdateAliasesRequest(aliasAction), RequestOptions.DEFAULT); }七、性能优化策略7.1 查询优化public ListProduct optimizedSearch(String keyword) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery(name, keyword)) .withFetchSource(new String[]{name, price, category}, null) .withTrackTotalHits(false) .build(); SearchHitsProduct hits elasticsearchOperations.search(query, Product.class); return hits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }7.2 批量操作public void bulkIndex(ListProduct products) { BulkOperations bulkOperations elasticsearchOperations.bulk(); products.forEach(product - bulkOperations.save(product) ); bulkOperations.execute(); }7.3 索引预热public void warmUpIndex(String indexName) { NativeSearchQuery query new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .withSize(100) .build(); elasticsearchOperations.search(query, Product.class); }八、错误处理与重试机制8.1 自定义异常处理RestControllerAdvice public class ElasticsearchExceptionHandler { ExceptionHandler(ElasticsearchException.class) public ResponseEntityMapString, String handleElasticsearchException( ElasticsearchException ex ) { MapString, String response new HashMap(); response.put(error, ex.getMessage()); response.put(status, FAILED); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(response); } }8.2 重试配置Configuration public class RetryConfig { Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate new RetryTemplate(); SimpleRetryPolicy retryPolicy new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(3); FixedBackOffPolicy backOffPolicy new FixedBackOffPolicy(); backOffPolicy.setBackOffPeriod(1000L); retryTemplate.setRetryPolicy(retryPolicy); retryTemplate.setBackOffPolicy(backOffPolicy); return retryTemplate; } }九、实战案例商品搜索服务9.1 Controller 层RestController RequestMapping(/api/products) public class ProductController { private final ProductService productService; public ProductController(ProductService productService) { this.productService productService; } PostMapping public ResponseEntityProduct create(RequestBody Product product) { Product saved productService.save(product); return ResponseEntity.status(HttpStatus.CREATED).body(saved); } GetMapping(/{id}) public ResponseEntityProduct getById(PathVariable String id) { return productService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } GetMapping(/search) public ResponseEntityListProduct search( RequestParam String keyword, RequestParam(required false) String category, RequestParam(required false) Double minPrice, RequestParam(required false) Double maxPrice ) { ListProduct products productService.advancedSearch( keyword, category, minPrice, maxPrice ); return ResponseEntity.ok(products); } GetMapping(/statistics/categories) public ResponseEntityMapString, Long getCategoryStatistics() { MapString, Long statistics productService.getCategoryStatistics(); return ResponseEntity.ok(statistics); } }9.2 测试用例SpringBootTest class ProductServiceTest { Autowired private ProductService productService; Test void testCRUDOperations() { Product product new Product(); product.setName(iPhone 15 Pro); product.setCategory(Electronics); product.setPrice(9999.0); product.setStock(100); product.setCreateTime(LocalDateTime.now()); Product saved productService.save(product); assertNotNull(saved.getId()); OptionalProduct found productService.findById(saved.getId()); assertTrue(found.isPresent()); assertEquals(iPhone 15 Pro, found.get().getName()); productService.deleteById(saved.getId()); assertFalse(productService.findById(saved.getId()).isPresent()); } Test void testSearch() { ListProduct results productService.searchProducts(phone); assertNotNull(results); } }十、总结本文详细介绍了 Spring Boot 与 Elasticsearch 8.x 的集成实践涵盖了从基础配置到高级特性的各个方面环境配置依赖引入、连接配置、客户端初始化文档操作实体定义、Repository 接口、CRUD 操作高级查询全文搜索、布尔查询、嵌套文档查询聚合分析统计聚合、分组聚合性能优化查询优化、批量操作、索引预热错误处理异常处理、重试机制通过本文的学习读者可以掌握 Spring Boot 与 Elasticsearch 集成的核心技能能够构建高效、可靠的搜索服务。在实际项目中还需要根据业务需求进行适当的调整和优化以达到最佳的性能和用户体验。