Elasticsearch 是一个基于 Lucene 的分布式搜索服务器,具有高效的全文检索能力。在现代应用中,尤其是需要强大搜索功能的系统中,Elasticsearch 被广泛使用。
Spring Boot 提供了对 Elasticsearch 的集成支持,使得开发者可以轻松地将 Elasticsearch 集成到 Spring Boot 应用中,实现高效的搜索、分析等功能。本文将详细介绍如何在 Spring Boot 中集成 Elasticsearch,并展示一些基本的使用示例以及通过 ElasticsearchRestTemplate 实现的高级功能。
一、准备工作
1. 安装 Elasticsearch
你可以通过以下方式安装 Elasticsearch:
- 使用 Docker 安装:
 
            
            
              bash
              
              
            
          
          docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.11.3
        - 直接下载安装包 :Elasticsearch 下载页面
 
确保 Elasticsearch 成功启动后,可以通过浏览器访问 http://localhost:9200 查看其状态信息。
二、创建 Spring Boot 项目
使用 Spring Initializr 创建一个 Spring Boot 项目,选择以下依赖:
- Spring Web
 - Spring Data Elasticsearch
 
或者手动在 pom.xml 中添加如下依赖(适用于 Maven 项目):
            
            
              xml
              
              
            
          
          <dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Data Elasticsearch -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <!-- Lombok(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
        三、配置 Elasticsearch
在 application.yml 或 application.properties 中配置 Elasticsearch 连接信息:
            
            
              yaml
              
              
            
          
          spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200
        如果你使用的是较老版本的 Spring Boot,可能需要手动配置 RestHighLevelClient,但在新版本中已默认使用 ElasticsearchRestTemplate 和 RestClient。
四、定义实体类
我们先定义一个简单的实体类,并使用注解来映射 Elasticsearch 索引结构。
            
            
              java
              
              
            
          
          import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "blog-post", shards = 1)
public class BlogPost {
    @Id
    private String id;
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String content;
    // 构造方法、getter/setter 省略
}
        注意:如果使用中文分词,建议使用
ik-analyzer插件,在 Elasticsearch 中安装后指定analyzer。
五、定义 Repository 接口
Spring Data 提供了 ElasticsearchRepository 接口,我们可以继承它来操作文档。
            
            
              java
              
              
            
          
          import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface BlogPostRepository extends ElasticsearchRepository<BlogPost, String> {
}
        六、编写 Service 层
            
            
              java
              
              
            
          
          import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class BlogPostService {
    @Autowired
    private BlogPostRepository blogPostRepository;
    public BlogPost save(BlogPost blogPost) {
        return blogPostRepository.save(blogPost);
    }
    public Optional<BlogPost> findById(String id) {
        return blogPostRepository.findById(id);
    }
    public Iterable<BlogPost> findAll() {
        return blogPostRepository.findAll();
    }
    public void deleteById(String id) {
        blogPostRepository.deleteById(id);
    }
    public List<BlogPost> searchByTitle(String title) {
        return blogPostRepository.findByTitle(title);
    }
}
        七、编写 Controller 层
            
            
              java
              
              
            
          
          import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/blog-posts")
public class BlogPostController {
    @Autowired
    private BlogPostService blogPostService;
    @PostMapping
    public BlogPost create(@RequestBody BlogPost blogPost) {
        return blogPostService.save(blogPost);
    }
    @GetMapping("/{id}")
    public BlogPost get(@PathVariable String id) {
        return blogPostService.findById(id).orElse(null);
    }
    @GetMapping
    public Iterable<BlogPost> getAll() {
        return blogPostService.findAll();
    }
    @DeleteMapping("/{id}")
    public void delete(@PathVariable String id) {
        blogPostService.deleteById(id);
    }
    @GetMapping("/search")
    public List<BlogPost> search(@RequestParam String title) {
        return blogPostService.searchByTitle(title);
    }
}
        八、补充内容:使用 ElasticsearchRestTemplate
除了使用 Repository,我们还可以通过 ElasticsearchRestTemplate 来执行更复杂的查询、聚合、高亮等操作。
1. 注入 ElasticsearchRestTemplate
            
            
              java
              
              
            
          
          @Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
        2. 构建基本查询(Match 查询)
            
            
              java
              
              
            
          
          import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
public List<BlogPost> searchByTitleWithTemplate(String keyword) {
    Criteria criteria = new Criteria("title").is(keyword);
    CriteriaQuery query = new CriteriaQuery(criteria);
    SearchHits<BlogPost> hits = elasticsearchRestTemplate.search(query, BlogPost.class);
    return hits.stream()
               .map(SearchHit::getContent)
               .collect(Collectors.toList());
}
        3. 使用 NativeSearchQueryBuilder 构建复杂查询
            
            
              java
              
              
            
          
          import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
public List<BlogPost> searchByContentWithMatchQuery(String keyword) {
    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("content", keyword);
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(matchQueryBuilder)
            .build();
    SearchHits<BlogPost> hits = elasticsearchRestTemplate.search(searchQuery, BlogPost.class);
    return hits.stream()
               .map(SearchHit::getContent)
               .collect(Collectors.toList());
}
        4. 聚合查询(Aggregation)
            
            
              java
              
              
            
          
          import org.elasticsearch.index.aggregation.AggregationBuilders;
import org.elasticsearch.index.aggregation.bucket.terms.TermsAggregationBuilder;
public void aggregateKeywordsInTitle() {
    TermsAggregationBuilder aggregation = AggregationBuilders.terms("keywords")
            .field("title.keyword") // 注意字段类型应为 keyword
            .size(10);
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withAggregations(aggregation)
            .build();
    SearchHits<BlogPost> hits = elasticsearchRestTemplate.search(searchQuery, BlogPost.class);
    Terms keywordsAgg = hits.getAggregations().get("keywords");
    for (Terms.Bucket entry : keywordsAgg.getBuckets()) {
        System.out.println(entry.getKey() + ":" + entry.getDocCount());
    }
}
        5. 高亮显示匹配内容
            
            
              java
              
              
            
          
          import org.elasticsearch.index.highlight.HighlightBuilder;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
public List<BlogPost> searchWithHighlight(String keyword) {
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("content"); // 对 content 字段进行高亮
    highlightBuilder.preTags("<strong>").postTags("</strong>");
    MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("content", keyword);
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(matchQuery)
            .withHighlightBuilder(highlightBuilder)
            .build();
    SearchHits<BlogPost> hits = elasticsearchRestTemplate.search(searchQuery, BlogPost.class);
    return hits.stream().map(hit -> {
        Map<String, List<String>> highlightFields = hit.getHighlightFields();
        if (highlightFields.containsKey("content")) {
            String highlightedContent = String.join("...", highlightFields.get("content"));
            hit.getContent().setContent(highlightedContent); // 替换内容为高亮版本
        }
        return hit.getContent();
    }).collect(Collectors.toList());
}
        6. 分页查询
            
            
              java
              
              
            
          
          public List<BlogPost> searchWithPagination(String keyword, int page, int size) {
    MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", keyword);
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(matchQuery)
            .withPageable(PageRequest.of(page, size))
            .build();
    SearchHits<BlogPost> hits = elasticsearchRestTemplate.search(searchQuery, BlogPost.class);
    return hits.stream().map(SearchHit::getContent).collect(Collectors.toList());
}
        九、测试 API
你可以使用 Postman 或 curl 测试以下接口:
POST /api/blog-posts:创建一篇博客GET /api/blog-posts/{id}:获取某篇博客GET /api/blog-posts:获取所有博客DELETE /api/blog-posts/{id}:删除博客GET /api/blog-posts/search?title=xxx:按标题搜索博客- 更多通过 
ElasticsearchRestTemplate支持的高级查询也可以通过新增接口调用。 
十、总结
本文详细介绍了如何在 Spring Boot 项目中集成 Elasticsearch,并实现了基本的增删改查操作。同时,通过 ElasticsearchRestTemplate 展示了构建复杂查询、聚合、高亮、分页等高级功能的方式。
通过 Spring Data Elasticsearch 提供的抽象,我们可以非常方便地进行数据持久化和检索。而 ElasticsearchRestTemplate 则为我们提供了更灵活的底层控制能力,非常适合用于构建企业级搜索功能。