SpringCloud(七.3)ES(elasticsearch)-- RestClient操作索引库、文档
RestClient是ES官方提供的各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。
官方地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
官方文档使用教程
使用RestClient操作索引库
使用案例:
hotel-demo 源代码资料 下载链接:https://pan.baidu.com/s/1cRprxQKKSRfO-DmrsuP2Hg 提取码:30yw
数据库sql 下载链接:https://pan.baidu.com/s/1SPJ8qi6wkHqnCZQ75pqT2A 提取码:6ur4
导入sql后我们在数据库可以看到一张 tb_hotel 的表
接下来我们尝试在kibana中编写这张酒店表的mapping
mapping分析
id -- 主键,它对应的数据类型比较特殊,在这里需要定义为字符串,因为它是文本且不需要分词,所以type为keyword,它需要被搜索index为true 走默认不用写。
name -- 名称,因为它需要分词而又是文本,所以type类型为 text,name肯定是做为主搜索项所以分词类型 analyzer 配置为 ik_max_word,它需要被搜索index为true 走默认不用写。
address -- 地址,它是文本且不需要分词所以type类型为keyword,根据实际情况考虑很少有人会根据地址去搜索酒店一般人查酒店都是按名称去查,所以它不需要被搜索index为 false。
price -- 价格,库表里定义成了int,所以它的type类型为integer,不涉及分词但会涉及到排序和搜索,所以index为true 走默认不用写
score -- 评分,同价格,库表里定义成了int,所以它的type类型为integer,不涉及分词但会涉及到排序,所以index为true 走默认不用写
brand -- 品牌,不涉及分词只是涉及搜索,所以它的type类型为keyword,它需要被搜索index为true 走默认不用写
city -- 所在城市,不涉及分词只是涉及搜索,所以它的type类型为keyword,它需要被搜索index为true 走默认不用写
starName -- 酒店星级,不涉及分词只是涉及排序或搜索,所以它的type类型为keyword,它需要被搜索index为true 走默认不用写 (这里是个大坑,数据库字段是star_Name,而实体中是starName,DSL中必须要与实体一致。否则后面检索查询时会导致查不到。)
business -- 商圈,不涉及分词只是涉及搜索,所以它的type类型为keyword,它需要被搜索index为true 走默认不用写
经度纬度在这里比较特殊它在ES中有两种数据类型表示 geo_point(点) 和 geo_shape(线),所以这里经度和纬度合并为一个字段它是一个点,所以type类型为geo_point
最后酒店图片,不涉及分词和搜索,所以type类型为keyword,index为false。
如果以后模糊搜索想根据 酒店名称、品牌、城市 等等这些字段去搜索,这时就会用到 copy_to 属性。
示例:
新建一个字段all,它用于模糊搜索。需要分词和搜索,所以type为text,分词类型 analyzer 为 ik_max_word,index为true 走默认不用写
给酒店名称、品牌、城市等等这些字段添加一个新的属性 copy_to 值为 all
DSL maaping展示:
#酒店索引创建 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", "copy_to": "all" }, "star_name":{ "type": "keyword" }, "business":{ "type": "keyword" }, "location":{ "type":"geo_point" }, "pic":{ "type":"keyword", "index": false }, "all":{ "type":"text", "analyzer": "ik_max_word" } } } }
初始化JavaRestClient
1、引入ES的RestHighLevelClient依赖
<!--elasticsearch--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency>
2、统一ES版本,因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本
<properties> <java.version>1.8</java.version> <elasticsearch.version>7.12.1</elasticsearch.version> </properties>
3、初始化RestHighLevelClient(连接到ES和释放掉ES)
@SpringBootTest class HotelIndexTest { private RestHighLevelClient client; @Test void testInit(){ System.out.println(client); } @BeforeEach void setUp() { client = new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.223.129:9200") )); } @AfterEach void tearDown() throws IOException { client.close(); } }
setUp为初始化RestClient,地址为 虚拟机/服务器ip地址:ES端口
tearDown为释放RestClient
创建索引库
@Test void testCreateIndex() throws IOException { // 1.准备Request PUT /hotel CreateIndexRequest request = new CreateIndexRequest("hotel"); // 2.准备请求参数, MAPPING_TEMPLATE是静态常量字符串,内容就是创建索引库的DSL语句(注意这里是从mappings开始没有第一行) request.source(MAPPING_TEMPLATE, XContentType.JSON); // 3.发送请求 client.indices().create(request, RequestOptions.DEFAULT); }
现在就差MAPPING_TEMPLATE常量了
我们在项目中新建一个constants文件夹,新建HotelIndexConstants用来存放DSL常量字符串
回到kibana 从mappings开始把内容复制进去
public class HotelIndexConstants { public static final String MAPPING_TEMPLATE = "{\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" + " \"copy_to\": \"all\"\n" + " },\n" + " \"star_name\":{\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"business\":{\n" + " \"type\": \"keyword\"\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" + "}"; }
执行 testCreateIndex
在kibana中查看一下创建的hotel索引
删除和判断索引库
查重索引库
@Test void testExistsIndex() throws IOException { // 1.准备Request GetIndexRequest request = new GetIndexRequest("hotel"); // 3.发送请求 boolean isExists = client.indices().exists(request, RequestOptions.DEFAULT); System.out.println(isExists ? "存在" : "不存在"); }
删除索引库
@Test void testDeleteIndex() throws IOException { // 1.准备Request DeleteIndexRequest request = new DeleteIndexRequest("hotel"); // 3.发送请求 client.indices().delete(request, RequestOptions.DEFAULT); }
删除之后再执行查重方法
总结:
使用RestClient操作文档
1、初始化RestHighLevelClient(连接到ES和释放掉ES)
@SpringBootTest class HotelDocumentTest { private RestHighLevelClient client; @BeforeEach void setUp() { client = new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.223.129:9200") )); } @AfterEach void tearDown() throws IOException { client.close(); } }
2、添加酒店数据到索引库
1)准备一条酒店数据,从数据库中随机拿一条数据,查看一下记录的id
2)因为DSL中的格式与数据库表格式略微不同(经纬度在ES中用geo_point),所以查询出来的酒店数据需要进行数据转换。
定义一个ES的数据结构实体HotelDoc
@Data @NoArgsConstructor public class HotelDoc { 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 HotelDoc(Hotel hotel) { this.id = hotel.getId(); this.name = hotel.getName(); this.address = hotel.getAddress(); this.price = hotel.getPrice(); this.score = hotel.getScore(); this.brand = hotel.getBrand(); this.city = hotel.getCity(); this.starName = hotel.getStarName(); this.business = hotel.getBusiness(); this.location = hotel.getLatitude() + ", " + hotel.getLongitude(); this.pic = hotel.getPic(); } }
@Test void testAddDocument() throws IOException { // 1.查询数据库hotel数据 Hotel hotel = hotelService.getById(61083L); // 2.转换为HotelDoc HotelDoc hotelDoc = new HotelDoc(hotel); // 3.转JSON String json = JSON.toJSONString(hotelDoc); // 1.准备Request 因为ES中id为keyword字符串所以这里要转一下toString IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString()); // 2.准备请求参数DSL,其实就是文档的JSON字符串 request.source(json, XContentType.JSON); // 3.发送请求 client.index(request, RequestOptions.DEFAULT); }
执行,在kibana中查看效果,如图:
3、利用JavaRestClient根据id查询酒店数据
@Test void testGetDocumentById() throws IOException { // 1.准备Request // GET /hotel/_doc/{id} GetRequest request = new GetRequest("hotel", "61083"); // 2.发送请求 GetResponse response = client.get(request, RequestOptions.DEFAULT); // 3.解析响应结果 String json = response.getSourceAsString(); HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); System.out.println("hotelDoc = " + hotelDoc); }
备注:在ES中文档数据存放在source里,所以 response.getSourceAsString();
4、利用JavaRestClient根据id修改酒店数据(这里是局部更新,全量更新和新增没有什么区别)
@Test void testUpdateById() throws IOException { // 1.准备Request UpdateRequest request = new UpdateRequest("hotel", "61083"); // 2.准备参数 request.doc( "price", "870" ); // 3.发送请求 client.update(request, RequestOptions.DEFAULT); }
5、利用JavaRestClient删除酒店数据
@Test void testDeleteDocumentById() throws IOException { // 1.准备Request // DELETE /hotel/_doc/{id} DeleteRequest request = new DeleteRequest("hotel", "61083"); // 2.发送请求 client.delete(request, RequestOptions.DEFAULT); }
总结:
文档批量导入功能
原理:多个request.add一起提交
@Test void testBulkRequest() throws IOException { // 查询所有的酒店数据 List<Hotel> list = hotelService.list(); // 1.准备Request BulkRequest request = new BulkRequest(); // 2.准备参数 for (Hotel hotel : list) { // 2.1.转为HotelDoc HotelDoc hotelDoc = new HotelDoc(hotel); // 2.2.转json String json = JSON.toJSONString(hotelDoc); // 2.3.添加请求 request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(json, XContentType.JSON)); } // 3.发送请求 client.bulk(request, RequestOptions.DEFAULT); }
去kibana中查看下效果