ElasticsearchRestTemplate操作Elasticsearch(增删改查,新手必看)
使用 ElasticsearchRepository 可以实现 Elasticsearch 数据的增删改查等功能。但是,如果涉及一些位置、高亮、聚合等复杂查询,可能 ElasticsearchRepository 就不太合适了。所以 Spring Data 提供了 ElasticsearchRestTemplate 来解决这个问题。
下面介绍如何使用 ElasticsearchRestTemplate 来操作 Elasticsearch。
ElasticsearchRestTemplate 继承 ElasticsearchOperations 接口,提供了如下的查询类来构建数据查询请求:
需要注意的是,之前的版本使用的是 ElasticsearchTemplate,但是 ElasticsearchTemplate 在 7.6.2 版本已经被废除了,取而代之的是 ElasticsearchRestTemplate。
接下来,调用 bulkIndex() 批量保存文档:
下面来看自定义删除条件,例如根据 bookName 删除,示例代码如下:
ElasticsearchRestTemplate 提供了 NativeQuery、StringQuery 和 CriteriaQuery,通过这 3 个 Query 构造各种文档查询方式:
下面通过示例演示这 3 个查询的使用。

图 1 NativeQuery的运行结果

图 2 StringQuery的运行结果

图 3 CriteriaQuery的运行结果
HighlightBuilder 通过 preTags() 和 postTags() 设置关键字高亮显示的效果,Field() 设置高亮显示的字段。具体示例代码如下:

图 4 HighlightQuery的运行结果
通过输出发现,category 字段匹配的关键字被加上高亮的样式。我们可以通过 getHighlightField 获取并处理高亮显示的字段。
下面介绍如何使用 ElasticsearchRestTemplate 来操作 Elasticsearch。
ElasticsearchRestTemplate简介
ElasticsearchRestTemplate 是 Spring Data Elasticsearch 提供的 Elasticsearch 数据操作类,基于 Elasticsearch 的 HTTP 协议,用来取代之前的 TransportClient 客户端。相信大家对 RedisTemplates 已经很熟悉,同样的 ElasticsearchRestTemplate 的目的是让我们用更简单的方式实现复杂的 Elasticsearch 数据查询操作。ElasticsearchRestTemplate 继承 ElasticsearchOperations 接口,提供了如下的查询类来构建数据查询请求:
- SearchQuery:普通数据查询类;
- BoolQueryBuilder:条件查询,可在后面加上 must、mustNot、should 等;
- MatchQueryBuilder:匹配查询;
- TermQueryBuilder:倒排索引查询;
- HighlightBuilder:高亮查询,用于设置要高亮的字段;
- SearchHit:查询结果。
需要注意的是,之前的版本使用的是 ElasticsearchTemplate,但是 ElasticsearchTemplate 在 7.6.2 版本已经被废除了,取而代之的是 ElasticsearchRestTemplate。
创建文档
ElasticsearchRestTemplate 提供了 index() 创建单个文档,同时提供了 bulkIndex() 批量创建文档。示例代码如下:private static final String Book_INDEX = "book"; @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; @Test public void testIndex() { Book book = new Book(); book.setId(9L); book.setBookName("雪中悍刀行"); book.setAuthor("烽火戏诸侯"); book.setCategory("网络小说"); book.setPrice(300); book.setPage(5000); IndexQuery indexQuery = new IndexQueryBuilder() .withId(book.getId().toString()) .withObject(book).build(); String documentId = elasticsearchRestTemplate .index(indexQuery, IndexCoordinates.of(Book_INDEX)); System.out.println(documentId); }在上面的示例中,首先构建 IndexQuery 对象,然后调用 index() 保存文档数据。其实最终是由 ElasticsearchRestTemplate 调用 Elasticsearch 的 HTTP 接口完成数据的保存。
接下来,调用 bulkIndex() 批量保存文档:
@Test public void testBulkIndex() { Book book = new Book(); book.setId(10L); book.setBookName("盗墓笔记"); book.setAuthor("南派三叔"); book.setCategory("网络小说"); book.setPrice(200); book.setPage(4000); List<Book> books = new ArrayList<>(); books.add(book); List<IndexQuery> queries = books.stream() .map(book1-> new IndexQueryBuilder() .withId(book1.getId().toString()) .withObject(book1).build()) .collect(Collectors.toList());; List<String> documentIds = elasticsearchRestTemplate.bulkIndex(queries,IndexCoordinates.of(Book_INDEX)); for (String documentId : documentIds) { System.out.println(documentId); } }从上面的示例可以看到,bulkIndex 与 index 调用方法类似,只是需要构建 List<IndexQuery>参数,最后返回的也是文档的 documentId。
更新文档
使用 ElasticsearchRestTemplate 更新文档也是一样的流程,首先构建 UpdateQuery,然后调用 update() 更新文档,示例代码如下:@Test public void testUpdate() { Book book = new Book(); book.setId(10L); book.setBookName("盗墓笔记"); book.setAuthor("南派三叔"); book.setCategory("网络小说"); book.setPrice(300); book.setPage(4000); // 构造updateQuery UpdateQuery updateQuery = UpdateQuery.builder("10") // 如果不存在就新增,默认为false .withDocAsUpsert(true) .withDocument(Document.parse(JSON.toJSONString(book))) .build(); UpdateResponse response = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of(Book_INDEX)); System.out.println(JSON.toJSONString(response)); }
删除文档
根据 documentId 删除文档:@Test public void testDeleteById() { String result = elasticsearchRestTemplate.delete("5", IndexCoordinates.of(Book_INDEX)); System.out.println(result); }
下面来看自定义删除条件,例如根据 bookName 删除,示例代码如下:
@Test public void testDeleteByBookName() { NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.termQuery("bookName", "三")) .build(); elasticsearchRestTemplate.delete(nativeSearchQuery,Book.class, IndexCoordinates.of("book")); }
查询文档
ElasticsearchRestTemplate 通过 search() 方法实现非常完善的文档查询功能。它的使用方式与 ElasticsearchRepository 的 query() 类似。首先构建 QueryBuilder 对象,然后将查询对象传入 search() 方法执行查询。ElasticsearchRestTemplate 提供了 NativeQuery、StringQuery 和 CriteriaQuery,通过这 3 个 Query 构造各种文档查询方式:
- NativeQuery:可以灵活地构建各种复查查询(如聚合、筛选和排序);
- StringQuery:使用JSON字符串来构建查询条件,和 Repository 中的 @Query 注解中的 JSON 字符串类似;
- CriteriaQuery:通过简单地连接和组合所要搜索的文档必须满足指定的条件来生成查询,而无须了解 Elasticsearch 查询的语法或基础知识。
下面通过示例演示这 3 个查询的使用。
1) NativeQuery
通过 QueryBuilder 构建 category 字段包含“历史”关键字的查询条件,然后使用 NativeSearchQueryBuilder 构建 NativeQuery,最后执行 search() 方法。示例代码如下:@Test public void testNativeSearchQuery() { QueryBuilder queryBuilder = QueryBuilders.matchQuery("category", "历史"); Query searchQuery = new NativeSearchQueryBuilder() .withQuery(queryBuilder) .build(); SearchHits<Book> bookSearchHits = elasticsearchRestTemplate.search (searchQuery,Book.class,IndexCoordinates.of(Book_INDEX)); bookSearchHits.getSearchHits().forEach(System.out::println); }单击 Run Test 或在方法上右击,选择 Run 'testNativeSearchQuery',运行单元测试方法,验证 NativeQuery 查询的效果,运行结果如下图所示:

图 1 NativeQuery的运行结果
2) StringQuery
使用 StringQuery 就是传入 JSON 查询条件,查询 bookName 为“史记”的数据。示例代码如下:@Test public void testStringQuery() { Query searchQuery = new StringQuery("{\n" + " \"match\": { \n" + " \"bookName\": { \"query\": \"史记\" } \n" + " } \n" + " }"); SearchHits<Book> bookSearchHits = elasticsearchRestTemplate.search (searchQuery,Book.class,IndexCoordinates.of(Book_INDEX)); bookSearchHits.getSearchHits().forEach(System.out::println); }单击 Run Test 或在方法上右击,选择 Run 'testStringQuery',运行单元测试方法,验证 StringQuery 查询的效果,运行结果如下图所示:

图 2 StringQuery的运行结果
3) CriteriaQuery
CriteriaQuery 通过 where、is、contains、and、or 等简单地连接和组合所要搜索的文档必须满足指定的条件来生成查询,而无须了解 Elasticsearch 查询的语法或基础知识。示例代码如下:@Test public void testCriteriaQuery() { // 构造条件 Criteria criteria = Criteria.where(new SimpleField("bookName")) .contains("明") .or(new SimpleField("author")) .contains("明"); CriteriaQuery criteriaQuery = new CriteriaQuery(criteria); SearchHits<Book> blogSearchHits = elasticsearchRestTemplate.search (criteriaQuery, Book.class); blogSearchHits.getSearchHits().forEach(System.out::println); }单击 Run Test 或在方法上右击,选择 Run 'testCriteriaQuery',运行单元测试方法,验证 StringQuery 查询的效果,运行结果如下图所示:

图 3 CriteriaQuery的运行结果
高亮显示
ElasticsearchRestTemplate 提供 HighlightBuilder 实现查询结果的关键字高亮显示,支持设置某些关键字高亮,可以设置 n 个高亮的关键字,最后的查询结果按照符合高亮条件的个数来排序,即优先展示高亮字段多的。HighlightBuilder 通过 preTags() 和 postTags() 设置关键字高亮显示的效果,Field() 设置高亮显示的字段。具体示例代码如下:
@Test public void testHighlightQuery() { QueryBuilder queryBuilder = QueryBuilders.matchQuery("category", "历史"); // 设置高亮效果 String preTag = "<font color='#dd4b39'>";//Google的色值 String postTag = "</font>"; Query searchQuery = new NativeSearchQueryBuilder() .withQuery(queryBuilder) .withHighlightFields(new HighlightBuilder.Field("category").preTags(preTag).postTags(postTag)).build(); SearchHits<Book> bookSearchHits = elasticsearchRestTemplate.search (searchQuery,Book.class,IndexCoordinates.of(Book_INDEX)); bookSearchHits.getSearchHits().forEach(System.out::println); }单击 Run Test 或在方法上右击,选择 Run 'testStringQuery',运行单元测试方法,验证数据高亮查询的效果,运行结果如下图所示:

图 4 HighlightQuery的运行结果
通过输出发现,category 字段匹配的关键字被加上高亮的样式。我们可以通过 getHighlightField 获取并处理高亮显示的字段。