1. 概述

在这篇文章中,我们将以代码驱动、实践导向的方式,带你快速上手 Spring Data Elasticsearch

你将学会如何在 Spring 项目中使用 Spring Data Elasticsearch 来实现文档的索引、搜索与查询。Spring Data Elasticsearch 是 Spring Data 家族的一员,封装了对 Elasticsearch 的访问,让开发者能更优雅地操作这个基于 Lucene 构建的开源搜索引擎。

虽然 Elasticsearch 支持无 schema 模式,但在实际开发中我们通常会定义好 mapping,明确字段类型。这样在索引文档时,Elasticsearch 才能根据类型进行分词、过滤等处理。例如,text 类型字段会按照 mapping 中的规则进行分析,我们也可以自定义分词器和过滤器。

为了方便演示,我们使用 Docker 启动一个 Elasticsearch 实例,当然,任何监听在 9200 端口的 Elasticsearch 实例都可以:

docker run -d --name es762 -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2

2. Spring Data 基础

Spring Data 的核心价值在于减少样板代码。比如,我们只需要定义一个继承自 ElasticsearchRepository 的接口,就可以直接获得 CRUD 操作的能力。

此外,Spring Data 还支持通过方法命名规则来自动生成查询实现,无需手动编写 Repository 实现类。

2.1. Maven 依赖

Spring Data Elasticsearch 提供了对 Elasticsearch 的 Java API 支持。使用前,我们需要在 pom.xml 中添加如下依赖:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-elasticsearch</artifactId>
    <version>4.0.0.RELEASE</version>
</dependency>

2.2. 定义 Repository 接口

创建 Repository 时,只需继承 Spring Data Elasticsearch 提供的接口,并指定实体类和主键类型即可。

注意:ElasticsearchRepository 继承自 PagingAndSortingRepository,这意味着它天然支持分页和排序功能。

在我们的例子中,分页功能会在自定义查询中派上用场:

public interface ArticleRepository extends ElasticsearchRepository<Article, String> {

    Page<Article> findByAuthorsName(String name, Pageable pageable);

    @Query("{\"bool\": {\"must\": [{\"match\": {\"authors.name\": \"?0\"}}]}}")
    Page<Article> findByAuthorsNameUsingCustomQuery(String name, Pageable pageable);
}
  • findByAuthorsName 方法会由 Spring Data 根据方法名自动生成查询逻辑,解析出要访问 authors 字段下的 name 属性。
  • findByAuthorsNameUsingCustomQuery 使用了 @Query 注解,定义了一个自定义的布尔查询,要求作者名严格匹配传入参数。

2.3. Java 配置

我们需要配置如何连接 Elasticsearch 实例,这可以通过继承 AbstractElasticsearchConfiguration 类来实现:

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.baeldung.spring.data.es.repository")
@ComponentScan(basePackages = { "com.baeldung.spring.data.es.service" })
public class Config extends AbstractElasticsearchConfiguration {

    @Bean
    @Override
    public RestHighLevelClient elasticsearchClient() {
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
            .connectedTo("localhost:9200")
            .build();

        return RestClients.create(clientConfiguration)
            .rest();
    }
}
  • @EnableElasticsearchRepositories 指定 Spring 扫描 Repository 的包路径。
  • 我们使用 RestHighLevelClient 与 Elasticsearch 通信,这是官方推荐的方式,兼容性好,利于后续升级。
  • ElasticsearchOperations Bean 由父类自动提供,用于执行各种操作。

3. 映射(Mappings)

Mapping 用于定义文档的 schema,防止字段类型被错误推断。

我们定义一个简单的文档类 Article,它的主键是 String 类型,并指定文档存储在名为 blog 的索引中,类型为 article

@Document(indexName = "blog", type = "article")
public class Article {

    @Id
    private String id;
    
    private String title;
    
    @Field(type = FieldType.Nested, includeInParent = true)
    private List<Author> authors;
    
    // standard getters and setters
}
  • 使用 @Field(type = FieldType.Nested) 标记 authors 字段,这样可以在索引时将 Author 对象嵌入到 Article 中。
  • includeInParent = true 表示嵌套对象的字段也会被包含在父文档中,便于查询。

4. 索引文档

Spring Data Elasticsearch 会根据实体类自动创建索引,但我们也可以通过代码手动创建:

elasticsearchOperations.indexOps(Article.class).create();

然后我们就可以添加文档:

Article article = new Article("Spring Data Elasticsearch");
article.setAuthors(asList(new Author("John Smith"), new Author("John Doe")));
articleRepository.save(article);

✅ 简单粗暴,直接 save 即可完成索引。

5. 查询

5.1. 基于方法名的查询

使用方法名定义查询逻辑,Spring Data 会在启动时解析方法签名并自动生成查询语句:

String nameToFind = "John Smith";
Page<Article> articleByAuthorName
  = articleRepository.findByAuthorsName(nameToFind, PageRequest.of(0, 10));
  • 通过 PageRequest.of(0, 10) 获取第一页(0-based),每页最多 10 条。
  • Page 对象还提供了总记录数、总页数等分页信息。

5.2. 自定义查询

除了方法名查询,还可以通过以下方式自定义查询:

使用 @Query 注解(见 2.2 示例)

使用 Query Builder

如果我们要查找标题中包含 “data” 的文章,可以使用 NativeSearchQueryBuilder 并加上 filter:

Query searchQuery = new NativeSearchQueryBuilder()
   .withFilter(regexpQuery("title", ".*data.*"))
   .build();
SearchHits<Article> articles = 
   elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog"));

6. 更新与删除

更新文档

首先需要检索出文档:

String articleTitle = "Spring Data Elasticsearch";
Query searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%"))
  .build();

SearchHits<Article> articles = 
   elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
Article article = articles.getSearchHit(0).getContent();

然后修改对象内容并保存:

article.setTitle("Getting started with Search Engines");
articleRepository.save(article);

删除文档

✅ 有多种删除方式:

  • 删除对象:
articleRepository.delete(article);
  • 根据 ID 删除:
articleRepository.deleteById("article_id");
  • 自定义删除方法(如 deleteByTitle):
articleRepository.deleteByTitle("title");

⚠️ 使用自定义 delete 方法时,记得在 Repository 中声明对应方法。

7. 小结

本文我们快速上手了 Spring Data Elasticsearch,涵盖了文档的索引、查询、更新与删除操作。同时,我们也学会了如何通过自定义查询来满足复杂需求。

虽然 Spring Data 提供了很多便利,但有时候为了性能和灵活性,还是需要我们自己写原生查询。踩坑不可怕,关键是掌握套路。

如需获取完整代码,可以访问 GitHub 项目地址


原始标题:Introduction to Spring Data Elasticsearch