Spring Boot 集成 Elasticsearch 实现全文搜索

James Park | 2026-08-27T20:55:53 | Spring Boot, Database

使用 Spring Data Elasticsearch 实现中文全文搜索功能,涵盖索引设计、IK 分词、高亮和搜索建议。

# Spring Boot 集成 Elasticsearch 全文搜索 ## 一、依赖配置 ```xml org.springframework.boot spring-boot-starter-data-elasticsearch ``` ```yaml # application.yml spring: elasticsearch: uris: http://localhost:9200 username: elastic password: changeme ``` ## 二、索引设计 ```java @Document(indexName = "articles") @Setting(settingPath = "elasticsearch/settings.json") public class ArticleDoc { @Id private Long id; @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") private String title; @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") private String content; @Field(type = FieldType.Keyword) private String author; @Field(type = FieldType.Keyword) private List tags; @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second) private LocalDateTime publishedAt; @Field(type = FieldType.Integer) private Integer viewCount; @CompletionField(maxInputLength = 100) private Completion suggest; } ``` ### 分析器配置 ```json { "analysis": { "analyzer": { "ik_max_word": { "type": "custom", "tokenizer": "ik_max_word", "filter": ["lowercase"] }, "ik_smart": { "type": "custom", "tokenizer": "ik_smart", "filter": ["lowercase"] } } } } ``` ## 三、Repository ```java public interface ArticleRepository extends ElasticsearchRepository { List findByTitleContaining(String keyword); List findByTagsIn(List tags); } ``` ## 四、高级搜索服务 ```java @Service public class SearchService { private final ElasticsearchOperations esOps; public SearchService(ElasticsearchOperations esOps) { this.esOps = esOps; } public SearchHits search(String keyword, int page, int size) { // 多字段搜索 + 权重 Query query = NativeQuery.builder() .withQuery(q -> q.bool(b -> b .should(s -> s.match(m -> m .field("title").query(keyword).boost(3.0f))) .should(s -> s.match(m -> m .field("content").query(keyword).boost(1.0f))) )) // 高亮 .withHighlightQuery(new HighlightQuery( new Highlight(List.of( new HighlightField("title"), new HighlightField("content") )), ArticleDoc.class )) // 分页 .withPageable(PageRequest.of(page, size)) // 排序:相关度优先,其次按时间 .withSort(Sort.by(Sort.Direction.DESC, "_score")) .build(); return esOps.search(query, ArticleDoc.class); } // 搜索建议(自动补全) public List suggest(String prefix) { Query query = NativeQuery.builder() .withQuery(q -> q.bool(b -> b .should(s -> s.prefix(p -> p .field("title").value(prefix))))) .withMaxResults(5) .build(); SearchHits hits = esOps.search(query, ArticleDoc.class); List suggestions = new ArrayList(); for (SearchHit hit : hits) { suggestions.add(hit.getContent().getTitle()); } return suggestions; } // 聚合统计 public Map tagStats() { Query query = NativeQuery.builder() .withAggregation("tag_count", Aggregation.of(a -> a.terms(t -> t .field("tags").size(20)))) .withMaxResults(0) .build(); SearchHits hits = esOps.search(query, ArticleDoc.class); // 解析聚合结果... Map result = new HashMap(); return result; } } ``` ## 五、数据同步 ```java @Component public class ArticleSyncListener { private final ArticleRepository esRepo; // 监听数据库变更事件,同步到 ES @EventListener public void onArticleCreated(ArticleCreatedEvent event) { ArticleDoc doc = convertToDoc(event.getArticle()); esRepo.save(doc); } @EventListener public void onArticleDeleted(ArticleDeletedEvent event) { esRepo.deleteById(event.getArticleId()); } } ``` ## 最佳实践 1. 使用 IK 分词器处理中文,索引时用 `ik_max_word`,搜索时用 `ik_smart` 2. 标题字段权重设高于内容 3. 数据库和 ES 数据同步使用事件驱动或消息队列 4. 设置合理的分片和副本数量

← Back to Blog