👨🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:详解SpringCloud微服务技术栈:ElasticSearch原理精讲、安装、实践
📚订阅专栏:微服务技术全家桶
希望文章对你们有所帮助
在前面已经学习了如何使用DSL语句去操作ElasticSearch的索引库和文档,现在需要用ES官方提供的RestClient,这个客户端本质就是组装DSL语句,通过http请求发送给ES,从而方便我们使用Java代码进行操作。
ElasticSearch实战1——RestClient操作索引库与文档
导入demo
sql文件和项目工程从网盘下,自行导入:
链接:https://pan.baidu.com/s/1CDgGJGvpSu0-s6bFfLWvVA?pwd=nrx2
提取码:nrx2
hotel数据结构分析
mapping映射要考虑的问题:字段名、数据类型、是否参与搜索、是否分词、若分词,分词器选什么?
所以,我们需要针对数据库的字段来做mapping映射:
这里面的信息在做mapping的时候,主要是要会选择是否要参与搜索、是否分词,这需要根据业务需求来实现,比如用户找酒店的时候肯定不会是搜索酒店的地址,因此酒店地址不应该加入搜索,也不应该分词。
需要注意的是,ES中对地理坐标的描述,并不是字符串也不是数字,它支持了2种坐标数据类型:
geo_point:由维度latitude和精度longitude确定的一个点
geo_shape:有多个geo_point组成的复杂几何图形
容易发现,里面有很多字段存在ES中,将来都要参与搜索,也就意味着当用户输入某个字段的时候需要根据这多个字段来搜,效率肯定不高。
而ES提供了一个功能,能够实现多字段搜索同时效率还高的,即字段拷贝。也就是使用copy_to属性将当前字段拷贝到指定字段,示例:
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
"brand":{
"type": "keyword",
"copy_to": "all"
}
这样我们就可以把需要搜索的字段全部联合到all里面,即可实现用户输入一个信息,多字段搜索,且效率是会大大提高的。
因此在dev tools中编写DSL代码:
# 酒店索引库及mapping映射
PUT /hotel
{
"mapping": {
"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"
},
"startName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
上面代码没有正式执行,用来做个基础样式,最终创建索引库还是要用ES的java客户端来实现,方便之后简化开发。
RestClient操作索引库
初始化RestClient
1、引入ES的RestHighLevelClient依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
2、SpringBoot默认的ES版本是7.6.2,因此需要覆盖:
3、初始化RestHighLevelClient:
public class HotelIndexTest {
private RestHighLevelClient client;
@Test
void testInit() {
System.out.println(client);
}
@BeforeEach //提前完成RestHighLevelClient对象的初始化
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.177.130:9200")
));
}
@AfterEach //结束后要销毁这个初始化
void tearDown() throws IOException {
this.client.close();
}
}
创建索引库
结合DSL语句会比较好看懂,代码如下:
@Test
void createHotelIndex() throws IOException {
//创建request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//准备请求的参数,为JSON格式
request.source(MAPPING_TEMPLATE, XContentType.JSON);
//发送请求,indices为index复数,可以获得操作索引库的所有对象,默认方式发送
client.indices().create(request, RequestOptions.DEFAULT);
}
其中MAPPING_TEMPLATE就是之前的DSL语句,复制过来当作静态常量来存储:
在DEV TOOLS中输入GET /hotel
即可查看是否成功创建:
删除和判断索引库
其实这个可以自行的去做,也挺容易实现的,毕竟indices已经将所有操作索引库的api都封装好了,按Ctrl+Shift+空格
即可查看所有方法。
删除索引库:
@Test
void testDeleteIndex() 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.out.println(exists ? "存在" : "不存在");
}
RestClient操作文档
写一个小demo,去数据库中查询酒店数据,导入到hotel索引库,从而实现酒店数据的CRUD。
需要先进行JavaRestClient的初始化,在之前有代码了,直接CV一下。
而对于文档的操作,不再需要indices了。
新增文档
这里需要用的方法是index,表示新增文档时候创建倒排索引。
private RestHighLevelClient client;
@Resource
private IHotelService hotelService;
@Test
void testAddDocument() throws IOException {
//根据id查询酒店
Hotel hotel = hotelService.getById(61083L);
/**
* Hotel对象中的地理位置是分为经度和纬度分别来存的,但是ES中是用geo_point来存的
* 因此需要先转换为文档类型HotelDoc
*/
HotelDoc hotelDoc = new HotelDoc(hotel);
//准备request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
//准备JSON文档,传进去的需要是JSON格式的字符串
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
//发送请求
client.index(request, RequestOptions.DEFAULT);
}
查询文档
根据id查询到的文档数据是JSON,需要反序列化为java对象:
@Test
void testGetDocumentById() throws IOException {
GetRequest request = new GetRequest("hotel", "61083");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
//source里面是这个hotel的相关数据
String json = response.getSourceAsString();
//将json反序列化为java对象
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println("hotelDoc = " + hotelDoc);
}
更新文档
之前讲解了全量更新和局部更新,这里只演示局部更新:
@Test
void testUpdateDocument() throws IOException {
UpdateRequest request = new UpdateRequest("hotel", "61083");
request.doc(
"price", "952",
"starName", "四钻"
);
client.update(request, RequestOptions.DEFAULT);
}
删除文档
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("hotel", "61083");
client.delete(request, RequestOptions.DEFAULT);
}
再次查询,返回null:
批量导入文档
之前的新增,都是一次就新增一条数据,现在利用JavaRestClient批量导入酒店数据到ES中。
思路:
1、利用mybatis-plus查询酒店数据
2、将查询到的酒店数据(Hotel)转换为文档类型数据(HotelDoc)
3、利用JavaRestClient中的Bulk批处理,实现批量新增文档
@Test
void testBulkRequest() throws IOException {
//批量查询酒店数据
List<Hotel> hotels = hotelService.list();
//创建request
BulkRequest request = new BulkRequest();
//将hotels转换成HotelDoc
for (Hotel hotel : hotels) {
HotelDoc hotelDoc = new HotelDoc(hotel);
//准备参数,添加多个新增的request
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON)
);
}
//发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
在dev tools使用GET /hotel/_search
进行批量查询: