‍作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
上期文章:详解SpringCloud微服务技术栈:ElasticSearch原理精讲、安装、实践
订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

在前面已经学习了如何使用DSL语句去操作ElasticSearch的索引库和文档,现在需要用ES官方提供的RestClient,这个客户端本质就是组装DSL语句,通过http请求发送给ES,从而方便我们使用Java代码进行操作。

ElasticSearch实战1——RestClient操作索引库与文档

  • 导入demo
  • hotel数据结构分析
  • RestClient操作索引库
    • 初始化RestClient
    • 创建索引库
    • 删除和判断索引库
  • 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;@Testvoid 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语句会比较好看懂,代码如下:

@Testvoid 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+空格即可查看所有方法。
删除索引库:

@Testvoid testDeleteIndex() throws IOException {DeleteIndexRequest request = new DeleteIndexRequest("hotel");client.indices().delete(request, RequestOptions.DEFAULT);}

判断索引库是否存在:

@Testvoid 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;@Resourceprivate IHotelService hotelService;@Testvoid 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对象:

@Testvoid 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);}

更新文档

之前讲解了全量更新和局部更新,这里只演示局部更新:

@Testvoid testUpdateDocument() throws IOException {UpdateRequest request = new UpdateRequest("hotel", "61083");request.doc("price", "952","starName", "四钻");client.update(request, RequestOptions.DEFAULT);}

删除文档

@Testvoid 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批处理,实现批量新增文档

@Testvoid testBulkRequest() throws IOException {//批量查询酒店数据List<Hotel> hotels = hotelService.list();//创建requestBulkRequest request = new BulkRequest();//将hotels转换成HotelDocfor (Hotel hotel : hotels) {HotelDoc hotelDoc = new HotelDoc(hotel);//准备参数,添加多个新增的requestrequest.add(new IndexRequest("hotel").id(hotelDoc.getId().toString()).source(JSON.toJSONString(hotelDoc), XContentType.JSON));}//发送请求client.bulk(request, RequestOptions.DEFAULT);}

在dev tools使用GET /hotel/_search进行批量查询: