一、RestClient案例准备
对es概念不熟悉的话,先去看上面的 '实用篇-ES-索引库和文档',不建议基础不牢就直接往下学
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求来发送给ES。
官方文档地址: https://www.elastic.co/guide/en/elasticsearch/client/index.html
下面就使用java程序进行操作es,不再像上面那样使用浏览器页面进行操作es
在下面会逐步完成一个案例: 下载提供的hotel-demo.zip压缩包,解压后是hotel-demo文件夹,是一个java项目工程文件,按照条件创建索引库,索引库名为hotel,mapping属性根据数据库结构定义。还要下载一个tb_hotel.sql文件,作为数据库数据
hotel-demo.zip下载:https://cowtransfer.com/s/36ac0a9f9d9043
tb_hotel.sql下载: https://cowtransfer.com/s/716f049850a849
第一步: 把tb_hotel.sql文件导入进你的数据库
第二步:idea打开解压后的文件
第三步:在application.yml中配置正确的信息
二、hotel数据结构分析
在es中,mapping要考虑的问题: 字段名、数据类型、是否参与搜索、是否分词、如果分词那么分词器是什么。
我们刚刚在mysql导入了tb_hotel.sql,里面有很多数据,我们需要基于这些数据结构,去分析并尝试编写对应的es的mapping映射
先看mysql中的数据类型(已有),如下
根据mysql的数据类型等信息,编写es(没有,自己对着上面的sql写的)。注意经纬度在es里面是geo_point类型,且经纬度是写在一起的
PUT /hotel
{
"mappings": {
"properties": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "ik_max_word"
},
"address":{
"type": "keyword",
"index":false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword"
},
"city":{
"type": "keyword"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword"
},
"location":{
"type": "geo_point"
},
"pic":{
"type":"keyword",
"index": false
}
}
}
}
可以看到name,brand,score,city,starName等字段都要参与搜索,也就是用户可能根据多个关键字搜,查询条件是多个值,这时候可以用es提供的copy_to属性,把这些字段都拷贝到这个字段中,就可以实现在一个字段中搜到多个字段的内容了
下面演示把名字(name)、品牌(brand)和商圈(business)三个字段拷贝到all字段,代码如下
PUT /hotel
{
"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"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type":"keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
三、初始化RestClient
操作主要是在idea的hotel-demo项目进行,hotel-demo项目(不是springcloud项目,只是springboot项目)是前面 '1. RestClient案例准备',跳过的可回去补
第一步: 在hotel-demo项目的pom.xml添加如下
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<!--引入es的RestHighLevelClient,版本要跟你Centos7里面部署的es版本一致-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
第二步: 在hotel-demo项目的src/test/java/cn.itcast.hotel目录新建HotelIndexTest类,写入如下
package cn.itcast.hotel;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class HotelIndexTest {
private RestHighLevelClient client;
@Test
void init(){
System.out.println(client);
}
@BeforeEach
void setUp(){
this.client = new RestHighLevelClient(RestClient.builder(
//指定你Centos7部署的es的主机地址
HttpHost.create("http://192.168.229.129:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
运行init方法,可以看到client的信息打印出来,表示初始化成功
四、创建索引库
不是通过kibana的浏览器控制台,通过DSL语句来进行操作es,在es里面创建索引库
而是通过上一节初始化的RestClient对象,在Java里面去操作es,创建es的索引库。根本不需要kibana做中间者
第一步: 在src/main/java/cn.itcast.hotel目录新建constants.HotelConstants类,里面写DSL语句,如下
其中长长的字符串就是我们在前面 '2. hotel数据结构分析' 里面写的。忘了怎么写出来的,可以回去看看
package cn.itcast.hotel.constants;
public class HotelConstants {
public static final String MAPPING_TAMPLATE = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\":{\n" +
" \"type\": \"keyword\",\n" +
" \"index\":false\n" +
" },\n" +
" \"price\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"starName\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"location\":{\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"pic\":{\n" +
" \"type\":\"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"all\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
}
第二步: 在hotel-demo项目的HotelIndexTest类,添加如下
@Test
void testCreateHotelIndex() throws IOException {
//创建Request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//请求参数,MAPPING_TEMPLATE是静态常量字符串,内容是创建索引库的DSL语句
request.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);
//发起请求
client.indices().create(request, RequestOptions.DEFAULT);
}
第三步: 确保下面的服务你都在Centos7里面启动了
systemctl start docker # 启动docker服务
docker restart es #启动elasticsearch容器
docker restart kibana #启动kibana容器
第四步: 验证。运行HotelIndexTest类的testCreateHotelIndex测试方法
第五步: 验证,浏览器打开http://192.168.229.129:5601
五、删除和判断索引库
1、删除索引库。在hotel-demo项目的HotelIndexTest类,添加如下。然后运行testDeleteHotelIndex方法
@Test
void testDeleteHotelIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
client.indices().delete(request,RequestOptions.DEFAULT);
}
2. 判断索引库是否存在,刚才我们已经运行过删除索引库了,现在我们来写一个测试来判断索引库是否存在
@Test
void testExistsHotelIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("hotel");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.err.println(exists?"索引库存在":"索引库不存在");
}
六、新增文档
案例: 去数据库查询酒店数据,把查询到的结果导入到hotel索引库(上一节我们已经创建一个名为hotel的索引库),实现酒店数据的增删改查
简单说就是先去数据查酒店数据,把结果转换成索引库所需要的格式(新增文档的DSL语法)然后写到索引库,然后在索引库对这些酒店数据进行增删改查
第一步:在HotelIndexTest写入如下
package cn.itcast.hotel;
import cn.itcast.hotel.constants.HotelConstants;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
public class HotelIndexTest {
private RestHighLevelClient client;
@Autowired
private IHotelService hotelService;
@Test
void init(){
System.out.println(client);
}
@Test
void testCreateHotelIndex() throws IOException {
//创建Request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//请求参数,MAPPING_TEMPLATE是静态常量字符串,内容是创建索引库的DSL语句
request.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);
//发起请求
client.indices().create(request, RequestOptions.DEFAULT);
}
@Test
void testDeleteHotelIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
client.indices().delete(request,RequestOptions.DEFAULT);
}
@Test
void testExistsHotelIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("hotel");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.err.println(exists?"索引库存在":"索引库不存在");
}
@BeforeEach
void setUp(){
this.client = new RestHighLevelClient(RestClient.builder(
//指定你Centos7部署的es的主机地址
HttpHost.create("http://192.168.229.129:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
@Test
void testAddDocument() throws IOException {
//根据id查询酒店数据
//注意数据库中bigint类型对应java中的long类型
Hotel hotel = hotelService.getById(60223L);
//转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//准备Request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
//准备Json文档
request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
//发送请求
client.index(request,RequestOptions.DEFAULT);
}
}
主要是新增这个方法
@Test
void testAddDocument() throws IOException {
//根据id查询酒店数据
//注意数据库中bigint类型对应java中的long类型
Hotel hotel = hotelService.getById(60223L);
//转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//准备Request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
//准备Json文档
request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
//发送请求
client.index(request,RequestOptions.DEFAULT);
}
第二步:文档新增成功,我们去浏览器查询一下
七、查询文档
不管我们学习什么样的文档操作,java中的代码和DSL中的语句非常相似,所以我们可以类比着DSL语句来编写java代码
具体操作如下,我们查询刚才添加的id为60223的文档信息
@Test
void testGetDocumentById() throws IOException {
//创建request对象
GetRequest request = new GetRequest("hotel","60223");
//发送请求
GetResponse response = client.get(request,RequestOptions.DEFAULT);
//解析结果
String res = response.getSourceAsString();
System.out.println(res);
}
八、更新文档
具体操作,把id为60223的文档信息更新一下,名字和城市
第一步:写入此方法,并运行
@Test
void testUpdateDocumentById() throws IOException {
//创建Request对象
UpdateRequest request = new UpdateRequest("hotel","60223");
//准备参数,每两个参数为一对key value
request.doc(
"name","上海梅赛德斯奔驰文化中心",
"city","阿布扎比"
);
//更新文档
client.update(request,RequestOptions.DEFAULT);
}
第二步:测试是否更新,在浏览器中Get请求
九、删除文档
经过以上类比,我们不难写出删除文档的代码
具体操作,删除id为60223的文档信息
@Test
void testDeleteDocumentById() throws IOException {
//创建Request对象
DeleteRequest request = new DeleteRequest("hotel","60223");
//删除文档
client.delete(request,RequestOptions.DEFAULT);
}
测试如下
十、批量导入文档
我们一直都是操作一条id为60223的文档(相当于数据库表的某一行)。我们如何把mysql的更多数据导入进es的索引库(相当于mysql的表)呢,下面就来学习批量把文档导入进索引库
思路:
1、利用mybatis-plus把MySQL中的酒店数据查询出来
2、将查询到的酒店数据转换为文档类型的数据
3、利用RestClient中bulk批处理方法,实现批量新增文档
第一步: 在HotelDocumentTest类,添加如下
@Test
void testBulkRequest() throws IOException {
//mp查出数据库中所有的信息
List<Hotel> hotelList = hotelService.list();
//转化为hotelDoc
List<HotelDoc> hotelDocList = hotelList.stream()
.map(new Function<Hotel, HotelDoc>() {
@Override
public HotelDoc apply(Hotel hotel) {
HotelDoc hotelDoc = new HotelDoc(hotel);
return hotelDoc;
}
}).collect(Collectors.toList());
//创建Bulk请求
BulkRequest bulkRequest = new BulkRequest();
hotelDocList.forEach(hotelDoc -> bulkRequest
.add(new IndexRequest("hotel").id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc),XContentType.JSON)));
//发送bulk请求
client.bulk(bulkRequest, RequestOptions.DEFAULT);
}
}
第二步:浏览器批量查询