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 项目地址。