RestClient操作 Elasticsearch(ES)索引库和文档
今日目标
掌握RestClient操作索引和文档
什么是RestClient
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

其中的Java Rest Client又包括两种:
-
Java Low Level Rest Client -
Java High Level Rest Client 我们学习的是Java HighLevel Rest Client客户端API
1. RestClient操作索引库项目准备
1.1. 项目准备
首先导入数据库数据elastic
数据结构如下
create database elastic;
use elastic;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_wine_shop
-- ----------------------------
DROP TABLE IF EXISTS `tb_wine_shop`;
CREATE TABLE `tb_wine_shop` (
`id` bigint(20) NOT NULL COMMENT '酒店id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店名称',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店地址',
`price` int(10) NOT NULL COMMENT '酒店价格',
`score` int(2) NOT NULL COMMENT '酒店评分',
`brand` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店品牌',
`city` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所在城市',
`star_name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '酒店星级,1星到5星,1钻到5钻',
`business` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商圈',
`latitude` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '纬度',
`longitude` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '经度',
`pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '酒店图片',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
1.2. 导入elastic-demo项目
项目地址:https://github.com/bangbangzhou/learn_springboot/tree/main/elastic-demo
项目结构如图:

1.3. mapping映射分析
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!
创建索引库,最关键的是mapping映射,而mapping映射要考虑的信息包括:
-
字段名 -
字段数据类型 -
是否参与搜索 -
是否需要分词 -
如果分词,分词器是什么? 其中: -
字段名、字段数据类型,可以参考数据表结构的名称和类型 -
是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索 -
是否分词呢要看内容,内容如果是一个整体就无需分词,反之则要分词 -
分词器,我们可以统一使用ik_max_word 来看下wineshop的索引库结构:
PUT /wineshop
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword",
"copy_to": "all"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
几个特殊字段说明:
-
location:地理坐标,里面包含精度、纬度 -
all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索地理坐标说明:ES中支持两种地理坐标数据类型: -
geo_point:由纬度(latitude)和经度(longitude)确定的一个点。例如:”32.8752345, 120.2981576″ -
geo_shape:有多个geo_point组成的复杂几何图形。例如一条直线,”LINESTRING (-77.03653 38.897676, -77.009051 38.889939)”
copy_to说明:字段拷贝可以使用copy_to属性将当前字段拷贝到指定字段。示例:
"all": {
"type": "text",
"analyzer": "ik_max_word"
},
"brand": {
"type": "keyword",
"copy_to": "all"
}
1.4.初始化RestClient
【步骤一】:引入es的RestHighLevelClient依赖:
因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:
<!-- elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.1</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.1</version>
</dependency>
【步骤二】:初始化RestHighLevelClient:
ES实例化RestHighLevelClient时指定用户名和密码
在com.zbbmeta.elastic.config
包下创建RestHighLevelClientConfig
类进入注入RestHighLevelClient到Spring容器
@Configuration
public class RestHighLevelClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
// 配置用户名和密码
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "123456"));
// 创建 RestHighLevelClient
return new RestHighLevelClient(
RestClient.builder( HttpHost.create("http://192.168.150.102:9200"))
.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
);
}
}
2. RestHighLevelClien操作ES索引库
2.1. RestHighLevelClient创建ES索引库
创建WineShopIndexTest
测试了,在测试类下创建索引库的代码如下:
/**
* 创建wineshop索引库
*/
@Test
public void createHotelIndex() throws IOException {
// 1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("wineshop");
// 2.准备请求的参数:DSL语句
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}
代码解读
-
1.创建Request对象。因为是创建索引库的操作,因此Request是CreateIndexRequest。 -
2.添加请求参数,其实就是DSL的JSON参数部分。因为json字符串很长,这里是定义了静态字符串常量MAPPING_TEMPLATE,让代码看起来更加优雅。 -
3.发送请求,client.indices()方法的返回值是IndicesClient类型,封装了所有与索库操作有关的方法。
使用DSL语句在控制台查看
# 查询wineshop索引
GET /wineshop

2.2. RestHighLevelClient删除ES索引库
删除索引库的DSL语句非常简单:
## 删除wineshop索引
DELETE /wineshop

删除ES索引库与创建索引库相比:
-
请求方式从PUT变为DELTE -
请求路径不变 -
无请求参数Java代码的差异,依然是三步走: -
-
创建Request对象。这次是DeleteIndexRequest对象 -
-
准备参数。这里是无参 -
-
发送请求。改用delete方法 在 WineShopIndexTest
测试类中,编写单元测试,实现删除索引:
/**
*
*删除wineshop索引库
*/
@Test
void testDeleteWineShopIndex() throws IOException {
// 1.创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("wineshop");
// 2.发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
2.3. RestHighLevelClient判断ES索引库是否存在
判断索引库是否存在,对应的DSL是:
## 判断wineshop索引是否存在
GET /wineshop
-
-
创建Request对象。这次是GetIndexRequest对象 -
-
准备参数。这里是无参 -
-
准备参数。这里是无参 在 WineShopIndexTest
测试类中,编写单元测试,实现判断ES索引库是否存在:
/**
*
* 判断ES索引库WineShop是否存在
*/
@Test
void testExistsWineShopIndex() throws IOException {
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("wineshop");
// 2.发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}
结果:

3.RestHighLevelClient操作文档
3.1. RestHighLevelClient新增ES文档
我们要将数据库的tb_wine_shop
数据查询出来,写入elasticsearch中。
索引库WineShop实体类
在 com.zbbmeta.elastic.entity
包下创建WineShop
实体类
package com.zbbmeta.elastic.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
/**
*
* @TableName tb_wine_shop
*/
@TableName(value ="tb_wine_shop")
@Data
@ToString
@EqualsAndHashCode
public class WineShop implements Serializable {
/**
* 酒店id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 酒店名称
*/
private String name;
/**
* 酒店地址
*/
private String address;
/**
* 酒店价格
*/
private Integer price;
/**
* 酒店评分
*/
private Integer score;
/**
* 酒店品牌
*/
private String brand;
/**
* 所在城市
*/
private String city;
/**
* 酒店星级,1星到5星,1钻到5钻
*/
private String starName;
/**
* 商圈
*/
private String business;
/**
* 纬度
*/
private String latitude;
/**
* 经度
*/
private String longitude;
/**
* 酒店图片
*/
private String pic;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
与我们的索引库结构存在差异:
-
longitude和latitude需要合并为location 因此,我们需要定义一个新的类型,与索引库结构吻合WineShopDoc类:
package com.zbbmeta.elastic.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class WineShopDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public WineShopDoc(WineShop wineShop) {
this.id = wineShop.getId();
this.name = wineShop.getName();
this.address = wineShop.getAddress();
this.price = wineShop.getPrice();
this.score = wineShop.getScore();
this.brand = wineShop.getBrand();
this.city = wineShop.getCity();
this.starName = wineShop.getStarName();
this.business = wineShop.getBusiness();
this.location = wineShop.getLatitude() + ", " + wineShop.getLongitude();
this.pic = wineShop.getPic();
}
}
新增ES文档语法说明
·新增文档的DSL语句如下:
POST /{索引库名}/_doc/1
{
"name": "springboot葵花宝典",
"age": 21
}
Java代码,依然是三步走:
-
-
创建Request对象 -
-
准备请求参数,也就是DSL中的JSON文档 -
-
发送请求变化的地方在于,这里直接使用client.xxx()的API,不再需要client.indices()了。在 WineShopIndexTest
测试类中,编写单元测试,实现新增ES文档:
/**
*
*新增WineShop下的文档数据
*/
@Test
void testAddWineShopDocument() throws IOException {
// 1.根据id查询酒店数据
WineShop wineShop = wineShopService.getById(61083L);
// 2.转换为文档类型
WineShopDoc wineShopDoc = new WineShopDoc(wineShop);
// 3.将WineShopDoc转json
String json = JSON.toJSONString(wineShopDoc);
// 1.准备Request对象
IndexRequest request = new IndexRequest("wineshop").id(wineShopDoc.getId().toString());
// 2.准备Json文档
request.source(json, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
}
结果:

3.2. RestHighLevelClient查询ES文档
查询ES文档语法说明
查询DSL语句如下
GET /wineShop/_doc/{id}
java非常简单 分两步:
-
准备Request对象 -
发送请求
不过查询的目的是得到结果,解析为WineShopDoc,因此难点是结果的解析。完整代码如下:
/**
*
* 查询WineShop下的id=61083文档数据
*/
@Test
void testGetWineShopDocumentById() throws IOException {
// 1.准备Request
GetRequest request = new GetRequest("wineshop", "61083");
// 2.发送请求,得到响应
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析响应结果
String json = response.getSourceAsString();
WineShopDoc wineShopDoc = JSON.parseObject(json, WineShopDoc.class);
System.out.println(wineShopDoc);
}
结果对比

3.3 RestHighLevelClient删除ES文档
删除的DSL语句如下:
DELETE /wineshop/_doc/{id}
和DSL查询相比,仅仅是请求方式从DELETE变成GET,可以想象Java代码应该依然是三步走:
-
-
准备Request对象,因为是删除,这次是DeleteRequest对象。要指定索引库名和id -
-
准备参数,无参 -
-
发送请求。因为是删除,所以是client.delete()方法
在WineShopIndexTest测试类中,编写单元测试:
/**
* 删除WineShop下的id=61083文档数据
*/
@Test
void testDeleteWineShopDocument() throws IOException {
// 1.准备Request
DeleteRequest request = new DeleteRequest("wineshop", "61083");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
}
3.4. RestHighLevelClient修改ES文档
修改文档两种方式:
-
全量修改:本质是先根据id删除,再新增 -
增量修改:修改文档中的指定字段值 在RestClient的API中,全量修改与新增的API完全一致,判断依据是ID: -
如果新增时,ID已经存在,则修改 -
如果新增时,ID不存在,则新增 这里不再赘述,我们主要关注增量修改。 在WineShopIndexTest测试类中,编写单元测试:
@Test
void testUpdateWineShopDocument() throws IOException {
// 1.准备Request
UpdateRequest request = new UpdateRequest("wineshop", "61083");
// 2.准备请求参数
request.doc(
"price", "952",
"starName", "四钻"
);
// 3.发送请求
client.update(request, RequestOptions.DEFAULT);
}
3.5. RestHighLevelClient批量导入文档
语法说明
批量处理BulkRequest,其本质就是将多个普通的CRUD请求组合在一起发送。其中提供了一个add方法,用来添加其他请求:
-
IndexRequest,也就是新增 -
UpdateRequest,也就是修改 -
DeleteRequest,也就是删除
因此Bulk中添加了多个IndexRequest,就是批量新增功能了。示例:Java代码,依然是三步走:
-
-
创建Request对象。这里是BulkRequest -
-
准备参数。批处理的参数,就是其它Request对象,这里就是多个IndexRequest -
-
发起请求。这里是批处理,调用的方法为client.bulk()方法
在WineShopIndexTest
测试类中,编写单元测试:
/**
*
* 批量导入wineShop文档
*/
@Test
void testBulkRequest() throws IOException {
// 批量查询酒店数据
List<WineShop> wineShops = wineShopService.list();
// 1.创建Request
BulkRequest request = new BulkRequest();
// 2.准备参数,添加多个新增的Request
for (WineShop wineShop : wineShops) {
// 2.1.转换为文档类型wineShopDoc
WineShopDoc hotelDoc = new WineShopDoc(wineShop);
// 2.2.创建新增文档的Request对象
request.add(new IndexRequest("wineshop")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
总结
JavaRestClient操作elasticsearch的流程基本类似。核心是client.indices()方法来获取索引库的操作对象。
索引库操作的基本步骤:
-
1.初始化RestHighLevelClient -
2.创建XxxIndexRequest。XXX是Create、Get、Delete -
3.准备DSL( Create时需要,其它是无参) -
4.发送请求。调用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete
文档操作的基本步骤:
-
1.初始化RestHighLevelClient -
2.创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk -
3.准备参数(Index、Update、Bulk时需要) -
4.发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk -
5.解析结果(Get时需要)
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原文始发于微信公众号(springboot葵花宝典):RestClient操作 Elasticsearch(ES)索引库和文档
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/182893.html