SpringBoot - Spring Data Elasticsearch使用详解1(使用ElasticsearchRestTemplate)
作者:hangge | 2025-05-27 09:14
我在之前的文章中介绍了如何使用 Elasticsearch 官方提供的 Java REST 客户端 Elasticsearch Rest Client 实现与 Elasticsearch 的集成(点击访问)。
而在 SpringBoot 项目中,我们还可以选择 Spring Data Elasticsearch 这个客户端,它集成于 Spring 生态系统,与 Spring Boot 等框架良好结合,提供了更多的开箱即用的功能。具体来说,Spring Data Elasticsearch 提供了两种不同方式用于与 Elasticsearch 进行交互的,分别是 ElasticsearchRestTemplate 和 ElasticsearchRepository。本文首先介绍前者的使用。
一、基本介绍
1,Spring Data Elasticsearch 介绍
(1)Spring Data Elasticsearch 是 Spring Data 项目的一部分,旨在提供对 Elasticsearch 的高级、更抽象的面向对象的数据访问。让我们可以像使用数据库一样,使用简单的 CRUD(创建、读取、更新、删除)操作来与 Elasticsearch 交互。
(2)Spring Data Elasticsearch 中提供了两种不同方式用于与 Elasticsearch 进行交互的,分别是 ElasticsearchRestTemplate 和 ElasticsearchRepository。
2,ElasticsearchRestTemplate 介绍
(1)ElasticsearchRestTemplate 是 Spring Data Elasticsearch 提供的一个强大的 Elasticsearch 客户端,它提供了直接与 Elasticsearch 进行交互的方法。
(2)使用 ElasticsearchRestTemplate,我们可以执行各种 Elasticsearch 操作,如索引创建、数据检索、更新和删除等。
(3)使用 ElasticsearchRestTemplate 这是一个相对底层的方式,我们需要编写自己的 Elasticsearch 查询语句,但也提供了更大的灵活性。
3,ElasticsearchRepository 介绍
(1)ElasticsearchRepository 是 Spring Data Elasticsearch 提供的一种高级抽象,它基于 Spring Data 的 Repository 模式,允许我们定义接口并使用 Spring Data 自动生成查询。
(2)通过继承 ElasticsearchRepository 接口,我们可以获得一些常见的 CRUD 操作,而无需编写太多的实现代码。
(3)这是一种更高层次的抽象,适用于简单的场景,但可能在复杂的查询上缺乏一些灵活性。
4,ElasticsearchRestTemplate 和 ElasticsearchRepository 的选择建议
(1)如果我们需要更多的灵活性,对 Elasticsearch 的操作有更多的控制,并且可以编写自己的查询语句,那么使用 ElasticsearchRestTemplate 可能更适合。
(2)如果我们只需要进行一些基本的 CRUD 操作,而且想要利用 Spring Data 提供的自动化功能,那么使用 ElasticsearchRepository 可能更方便。
二、安装配置
1,添加依赖
在 Spring Boot 项目的 pom.xml 文件中添加相关依赖:
提示:spring-boot-starter-data-elasticsearch 这个依赖会包含 Elasticsearch 的 Java API 和 Spring Data Elasticsearch 模块。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2,配置连接信息
在项目的 application.properties 文件中配置 Elasticsearch 的连接信息:
spring.elasticsearch.rest.uris=192.168.60.9:9200,192.168.60.10:9200,192.168.60.11:9200
三、使用 ElasticsearchRestTemplate 通过主键实现索引的增、删、改、查
1,创建实体类
在使用 Spring Data Elasticsearch 时,我们需要使用注解来定义实体类,从而告诉 Spring Data Elasticsearch 如何映射数据到 Elasticsearch 中的文档。下面我们创建一个表示图书信息的 Book 实体类。
注意:书籍的标题字段 title 和简介字段 introduction 配置了 IK 分词器以支持后续的中文检索。ik_max_word 和 ik_smart 区别:
- ik_max_word 会将文本做最细粒度的拆分。
- ik_smart 会做最粗粒度的拆分。
@Document(indexName = "book")
@Data
public class Book {
@Id
private Long id; // 书籍的唯一标识符
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; // 书籍的标题
@Field(type = FieldType.Keyword)
private String author; // 书籍的作者
private Integer price; // 书籍的价格
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String introduction; // 书籍的简介
}
2,新增数据
(1)下面代码使用 ElasticsearchRestTemplate 来新增一条数据:
@RestController
public class BookController {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@GetMapping("/test")
public void test() {
// 添加书籍数据
Book book = new Book();
book.setId(1L); // 如果索引不存在,新增文档时自动创建索引
book.setTitle("汉书");
book.setAuthor("班固");
book.setPrice(268);
book.setIntroduction("《汉书》是中国第一部纪传体断代史,全书记述了自西汉的汉高祖元年" +
"至新朝王莽地皇四年,共二百三十年史事。");
restTemplate.save(book);
}
}
(2)ElasticsearchRestTemplate 同样支持批量新增数据:
// 批量添加书籍数据
ArrayList<Book> books = new ArrayList<>();
Book book1 = new Book();
book1.setId(2L);
book1.setTitle("史记");
book1.setAuthor("司马迁");
book1.setPrice(28);
book1.setIntroduction("《史记》是我国著名史学家司马迁所著的史学巨著,列“二十四史”之首," +
"记载了从传说中的黄帝开始一直到汉武帝元狩元年(前122)三千年左右的历史," +
"被誉为“史家之绝唱,无韵之《离骚》”。读中国历史,不能不读《史记》。");
books.add(book1);
Book book2 = new Book();
book2.setId(3L);
book2.setTitle("资治通鉴");
book2.setAuthor("司马光");
book2.setPrice(498);
book2.setIntroduction("《资治通鉴》是中国古代著名的编年体史书,记载了战国至五代期间1300多年的历史," +
"向为史学界所推崇,在收集史料、考订事实及文字剪裁润色等方面都代表了古代编年体史书的最高成就");
books.add(book2);
restTemplate.save(books);
3,更新数据
(1)更新数据可以分为全部更新和局部更新。全部更新同添加数据,使用 save 方法。如果指定 ID 的索引数据(文档)已经存在,则执行更新操作。
// 更新书籍数据
Book book = new Book();
book.setId(1L);
book.setTitle("战争与和平");
book.setAuthor("托尔斯泰");
book.setPrice(88);
book.setIntroduction("《战争与和平》描写1812年俄法战争的全过程,以当时四大贵族家庭的人物活动为线索," +
"反映了1805至1820年间许多重大的历史事件,以及各阶层的现实生活。");
restTemplate.save(book);
(2)使用 update 方法可以进行部分数据的更新:
UpdateQuery query =UpdateQuery.builder("1")
.withDocument(Document.create().append("price", 888).append("author", "(汉)班固"))
.build();
UpdateResponse response = restTemplate.update(query, IndexCoordinates.of("book"));
System.out.println(response.getResult());
4,删除数据
下面代码删除类型对应的索引中的指定主键数据,返回删除数据的主键。注意:Elasticsearch 中的文档主键都是字符串类型的。
String response = restTemplate.delete("1", Book.class);
System.out.println(response);
5,根据主键查询数据
下面代码查询主键为 1 的文档数据:
Book book = restTemplate.get("1", Book.class);
System.out.println(book);
四、使用 ElasticsearchRestTemplate 通过 Query 接口搜索数据
1,基本介绍
在 SearchOperations 中定义的几乎所有方法都使用 Query 参数,该参数定义了要执行的查询以进行搜索。Query 是一个接口,Spring Data Elasticsearch 提供了三个实现:
- CriteriaQuery
- StringQuery
- NativeSearchQuery
2,CriteriaQuery 的使用
(1)基于 CriteriaQuery 的查询允许创建查询来搜索数据,而无需了解 Elasticsearch 查询的语法或基础知识。它们允许用户通过简单地链接和组合指定搜索文档必须满足的条件的条件对象来构建查询。以下是一些常用的 Criteria 查询条件:
// is(等于)
Criteria criteria = new Criteria("fieldName").is(value);
// not(不等于)
Criteria criteria = new Criteria("fieldName").not().is(value);
// in(包含在数组/集合中)
Criteria criteria = new Criteria("fieldName").in(values);
// notIn(不包含在数组/集合中)
Criteria criteria = new Criteria("fieldName").not().in(values);
// between(在两个值之间)
Criteria criteria = new Criteria("fieldName").between(minValue, maxValue);
// greaterThan(大于)
Criteria criteria = new Criteria("fieldName").greaterThan(value);
// greaterThanOrEquals(大于等于)
Criteria criteria = new Criteria("fieldName").greaterThanOrEquals(value);
// lessThan(小于)
Criteria criteria = new Criteria("fieldName").lessThan(value);
// lessThanOrEquals(小于等于)
Criteria criteria = new Criteria("fieldName").lessThanOrEquals(value);
// startsWith(以某个前缀开始)
Criteria criteria = new Criteria("fieldName").startsWith(prefix);
// endsWith(以某个后缀结束)
Criteria criteria = new Criteria("fieldName").endsWith(suffix);
// contains(包含某个子串)
Criteria criteria = new Criteria("fieldName").contains(substring);
// exists(存在某个字段)
Criteria criteria = new Criteria("fieldName").exists();
// isNull(字段为空)
Criteria criteria = new Criteria("fieldName").isNull();
// isNotNull(字段不为空)
Criteria criteria = new Criteria("fieldName").isNotNull();
// and(与逻辑)
Criteria criteria = new Criteria("field1").is(value1).and("field2").is(value2);
// or(或逻辑)
Criteria criteria = new Criteria("field1").is(value1).or("field2").is(value2);
(2)下面代码查询所有的数据:
// 不添加查询条件,即查询所有数据
Criteria criteria = new Criteria();
// 构建 CriteriaQuery
Query query = new CriteriaQuery(criteria);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(3)下面是一个简单条件查询的样例:
// 添加查询条件
Criteria criteria = new Criteria("author").is("司马光");
// 构建 CriteriaQuery
Query query = new CriteriaQuery(criteria);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(4)通过构建条件链可以实现组合查询:
// 添加查询条件
Criteria criteria = new Criteria("author").contains("司").and("price").lessThan(500);
// 构建 CriteriaQuery
Query query = new CriteriaQuery(criteria);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(5)我们可以通过嵌套使用 Criteria 对象来实现子查询的效果:
// 添加查询条件
Criteria criteria1 = new Criteria("author").is("司马迁").and("title").is("史记");
Criteria criteria2 = new Criteria("author").is("班固").and("title").is("汉书");
Criteria combinedCriteria = new Criteria().and(criteria1).or(criteria2);
// 构建 CriteriaQuery
Query query = new CriteriaQuery(combinedCriteria);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
3,StringQuery 的使用
(1)StringQuery 可以将 JSON 字符串作为一个 Elasticsearch 查询,下面代码查询所有的数据:
// 查询所有数据
Query query = new StringQuery("{\n" +
" \"match_all\": {}\n" +
"}");
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(2)下面是一个简单条件查询样例:
// 添加查询条件
Query query = new StringQuery("{\n" +
" \"match\": {\n" +
" \"author\": \"班固\"\n" +
" }\n" +
"}");
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(3)下面是一个多种条件组合的复杂样例:
// 添加查询条件
Query query = new StringQuery("{\n" +
" \"bool\": {\n" +
" \"must\": [\n" +
" {\n" +
" \"range\": {\n" +
" \"price\": {\n" +
" \"gte\": 0,\n" +
" \"lte\": 800\n" +
" }\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"must_not\": [\n" +
" {\n" +
" \"match\": {\n" +
" \"author\": \"班固\"\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
"}");
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(4)下面是一个增加分页查询条件的样例:
// 添加查询条件
Query query = new StringQuery("{\n" +
" \"range\": {\n" +
" \"price\": {\n" +
" \"gte\": 0,\n" +
" \"lte\": 800\n" +
" }\n" +
" }\n" +
"}", PageRequest.of(0, 2));
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(5)下面是一个增加排序条件的样例:
// 添加查询条件
Query query = new StringQuery("{\n" +
" \"range\": {\n" +
" \"price\": {\n" +
" \"gte\": 0,\n" +
" \"lte\": 800\n" +
" }\n" +
" }\n" +
"}", PageRequest.of(0, 2), Sort.by(Sort.Direction.DESC, "price"));
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(6)下面是高亮查询的样例:
提示:默认分词器会将每个中文文字分成一个词,这里我们使用了 IK 分词器进行中文内容的分词。
// 添加查询条件
Query query = new StringQuery("{\n" +
" \"match\": {\n" +
" \"introduction\": {" +
" \"query\": \"中国历史\"," +
" \"analyzer\": \"ik_max_word\"" +
" }" +
" }\n" +
"}");
// 创建高亮查询
HighlightQuery highlightQuery = new HighlightQuery(
new HighlightBuilder()
.field("introduction")
.preTags("<strong>")
.postTags("</strong>")
);
query.setHighlightQuery(highlightQuery);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
System.out.println("\n" + hit.getContent().getTitle());
List<String> highlightField = hit.getHighlightField("introduction");
highlightField.forEach(System.out::println);
}
} else {
System.out.println("未找到符合条件的书籍。");
}
4,NativeSearchQuery 的使用
(1)NativeSearchQuery 是在具有复杂查询或无法使用 Criteria API 表示的查询时使用的类,例如在构建查询和使用聚合时。它允许使用 Elasticsearch 库中所有不同的 QueryBuilder 实现,因此被命名为“原生”。
(2)下面是查询所有的数据:
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.matchAllQuery()
);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(3)下面是一个简单的条件查询示例:
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.matchQuery("author", "班固")
);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(4)下面是一个多种条件组合的复杂样例:
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.boolQuery()
.must(
QueryBuilders.rangeQuery("price").gte(0).lte(800)
)
.mustNot(
QueryBuilders.matchQuery("author", "班固")
)
);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(5)下面是一个增加分页查询条件的样例:
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.rangeQuery("price").gte(0).lte(800)
);
query.setPageable(PageRequest.of(0, 2));
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(6)下面是一个增加排序条件的样例:
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.rangeQuery("price").gte(0).lte(800)
);
query.addSort(Sort.by(Sort.Direction.DESC, "price"))
.setPageable(PageRequest.of(0, 2));
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
Book book = hit.getContent();
System.out.println("书名:" + book.getTitle() + " 作者:" + book.getAuthor()
+ " 价格:" + book.getPrice());
}
} else {
System.out.println("未找到符合条件的书籍。");
}

(7)下面是高亮查询的样例:
提示:默认分词器会将每个中文文字分成一个词,这里我们使用了 IK 分词器进行中文内容的分词。
// 构建NativeSearchQuery
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.matchQuery("introduction", "中国历史").analyzer("ik_max_word")
);
// 创建高亮查询
query.setHighlightQuery(
new HighlightQuery(
new HighlightBuilder()
.field("introduction")
.preTags("<strong>")
.postTags("</strong>")
)
);
// 使用 RestTemplate 发起查询
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 处理查询结果
if (hits.getTotalHits() > 0) {
System.out.println("查询结果总数:" + hits.getTotalHits());
for (SearchHit<Book> hit : hits) {
System.out.println("\n" + hit.getContent().getTitle());
List<String> highlightField = hit.getHighlightField("introduction");
highlightField.forEach(System.out::println);
}
} else {
System.out.println("未找到符合条件的书籍。");
}
(8)聚合查询是 NativeSearchQuery 的一大优势,下面代码通过聚合获取了"author"字段的词项聚合信息,然后遍历每个桶,输出了键(词项)和文档数量。
// 创建一个全匹配查询
NativeSearchQuery query = new NativeSearchQuery(
QueryBuilders.matchAllQuery()
);
// 向查询添加聚合(Aggregation),这里创建一个以 "author" 字段为基础的词项(terms)聚合
query.addAggregation(
AggregationBuilders.terms("author_agg").field("author")
);
// 使用 RestTemplate 发起查询,搜索类型为 Book 类型,索引名称为 "book"
SearchHits<Book> hits = restTemplate.search(query, Book.class, IndexCoordinates.of("book"));
// 获取查询结果中的聚合信息
Aggregations aggregations = hits.getAggregations();
// 从聚合信息中获取名为 "author_agg" 的词项聚合
ParsedStringTerms terms = aggregations.get("author_agg");
// 获取词项聚合中的桶(buckets)
List<? extends Terms.Bucket> buckets = terms.getBuckets();
// 遍历每个桶,输出桶的键(key,即作者名)和文档数量(docCount)
buckets.forEach(e -> System.out.println(e.getKeyAsString() + ":" + e.getDocCount()));
全部评论(0)