SpringCloud(8)— 使用ElasticSearch(RestClient)

news/2024/11/23 16:50:21/

SpringCloud(8)— 使用ElasticSearch(RestClient)

一 认识RestClient

ES 官方提供了各种语言的客户端用来操作ES,这些客户端的本质就是组创DSL语句,通过 Http 请求发送给ES

官方文档地址:Elasticsearch Clients | Elastic

提示1:ES 中支持两种地理坐标数据类型

  • geo_point:ES中的 mapping 类型中利用经度和纬度确定一个点
  • geo_shape:由多个 geo_point 组成的复杂几何图形

提示2:字段拷贝可以使用 copy_to 属性将当前字段的属性值拷贝到指定的属性上,方便以后搜索,且被指定的字段在查询数据时对外不可见

例如:以下示例中的 all 字段,在后边查询数据时不会返回

{"all":{"type":"text","analyzer":"ik_max_word"},"brand":{"type":"keyword","copy_to":"all"}
}

二 RestClient操作索引库

1.初始化RestClient

在项目中引入 RestClient 的 mavne 依赖坐标,版本需与 ElasticSearch 的版本一致。这里使用的是 7.12.1

<properties><elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<dependencies><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>${elasticsearch.version}</version></dependency>
</dependencies>

由于 SpringBoot 项目中已经对 elasticsearch 进行管理,所以需要手动定义相同的参数值去覆盖默认的版本号,从而使用我们想使用的版本

编写测试代码,测试 RestHighLevelClient 是否初始化成功

private RestHighLevelClient restHighLevelClient;//初始化 RestHighLevelClient 对象
@BeforeEach
void setUp(){this.restHighLevelClient=new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.119.101:9200")));
}//销毁 RestHighLevelClient 对象
@AfterEach
void tearDown() throws IOException {this.restHighLevelClient.close();
}@Test
void testInit(){System.out.println(restHighLevelClient);
}

或者可以使用 Ioc 思想,创建一个 Bean 到内存中,然后使用它即可

@Bean
public RestHighLevelClient restHighLevelClient(){return new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.119.101:9200")));
}

2.创建索引库

编写一个测试方法,用来测试创建索引库是否成功

@Test
void testCreateIndex() throws IOException {//1.创建一个 CreateIndexRequest 对象,指定 indexName(索引库名称)CreateIndexRequest request = new CreateIndexRequest("hotel");// 这是一段很恶心的 DSL 语句的拼接,为了方便可以将这一块代码提了出来,这里为了方便演示不做优化处理String mapping ="{\n" +"  \"mappings\": {\n" +"    \"properties\": {\n" +"      \"all\":{\n" +"        \"type\": \"text\"\n" +"      },\n" +"      \"id\":{\n" +"        \"type\": \"long\",\n" +"        \"index\": false\n" +"      },\n" +"      \"name\":{\n" +"        \"type\": \"text\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"address\":{\n" +"        \"type\": \"text\",\n" +"        \"index\": false, \n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"price\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"score\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"brand\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"city\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"starName\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"business\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"location\":{\n" +"        \"type\": \"geo_point\"\n" +"      },\n" +"      \"pic\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      }\n" +"    }\n" +"  }\n" +"}";//2.定义 DSLrequest.source(mapping, XContentType.JSON);//3.创建索引库restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
}

运行完成后,可以去浏览器通过 GET 方法访问,如果返回对应的JSON格式信息,则说明创建索引库成功。

# 浏览器访问
http://192.168.119.101:9200/hotel
# kibana 中的 dev tools 中测试
GET /hotel

CreateIndexRequest 对象中的 indices() 对象里,包含了操作索引库的所有方法

3.删除索引库

@Test
void testDeleteIndex() throws IOException {// 定义 DeleteIndexRequest 对象DeleteIndexRequest request=new DeleteIndexRequest("hotel");// 执行删除操作restHighLevelClient.indices().delete(request,RequestOptions.DEFAULT);
}

4.判断指定索引库是否存在

@Test
void testDeleteExist() throws IOException {// 定义 GetIndexRequest 对象GetIndexRequest request=new GetIndexRequest("hotel");// 发送请求restHighLevelClient.indices().exists(request,RequestOptions.DEFAULT);
}

三 RestClient操作文档

1.添加文档

编写测试方法,从数据库查询到指定数据,并且格式化为 Json,然后将数据存储到 特定的索引库中

Hotel 实体对象示例:

@Data
public class Hotel implements Serializable {private final static Long serialVersionUID=1L;private Long id;private String name;private String address;private Integer price;private Integer score;private String city;private String starName;private String brand;private String business;private String latitude;private String longitude;private String pic;
}

HotelDoc 文档实体对象:

注意:文档的实体应该与创建时的 mapping 字段完全一致

@Data
public class HotelDoc implements Serializable {public HotelDoc() {}public HotelDoc(Hotel hotel) {BeanUtils.copyProperties(hotel,this);// 主要是此处处理了经纬度的格式,使用字符串格式时,要求 纬度在前,经度在后this.location=hotel.getLatitude()+","+hotel.getLongitude();}private final static Long serialVersionUID = 1L;private Long id;private String name;private String address;private Integer price;private Integer score;private String city;private String starName;private String brand;private String business;private String pic;private String location;}

添加一条文档到索引库中:

@Test
void testAddDocument() throws IOException {Long id = 36934L;Hotel hotel = hotelService.getById(id);if (Objects.nonNull(hotel)) {// 1.创建 IndexRequest 对象,指定文档要存入的【索引库】及【文档Id】,此处的【文档Id】使用数据库的【主键Id】IndexRequest request = new IndexRequest("hotel").id(id.toString());// 2.准备 JSON 数据HotelDoc hotelDoc = new HotelDoc(hotel);String jsonString = JSON.toJSONString(hotelDoc);request.source(jsonString, XContentType.JSON);// 3.发送请求restHighLevelClient.index(request, RequestOptions.DEFAULT);return;}System.out.println("id=" + id + "的信息不存在");
}

踩坑底:对于 mapping 类型为 geo_point 的经纬度,使用字符串格式时,需要“纬度在前经度在后

运行成功后,数据将被存入到 ES 中

2.查看文档

编写查看文档的示例代码,重点API 【GetRequest】

@Test
void testGetDocument() throws IOException {Long id = 36934L;// 1.创建 GetRequest 对象,指定【索引库】和【文档ID】GetRequest request = new GetRequest("hotel", id.toString());// 2.发送请求,读取到数据GetResponse documentFields = restHighLevelClient.get(request, RequestOptions.DEFAULT);String sourceStr = documentFields.getSourceAsString();//3. 使用fastJson转为实体对象HotelDoc hotelDoc = JSON.parseObject(sourceStr, HotelDoc.class);System.out.println(hotelDoc);
}

3.修改文档

编写修改文档的示例代码,重点API 【UpdateRequest】

@Test
void testUpdateDocument() throws IOException {Long id=36934L;// 1.创建 UpdateRequest 对象,指定【索引库】和 【文档Id】UpdateRequest request=new UpdateRequest("hotel",id.toString());// 2.准备要更新的参数和值,每两个参数为一对 key valuerequest.doc("price",200,"name","8天假日酒店");// 3.发送请求restHighLevelClient.update(request,RequestOptions.DEFAULT);
}

4.删除文档

编写删除文档的示例代码,重点API 【DeleteRequest】

@Test
void testDeleteDocument() throws IOException {Long id=36934L;// 1.创建 DeleteRequest 对象,指定【索引库】和 【文档Id】DeleteRequest deleteRequest=new DeleteRequest("hotel",id.toString());// 2.发送请求restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
}

5.批量导入文档

编写批量导入测试方法,重点API 【BulkRequest】

@Test
void tesBulkDocument() throws IOException {// 1.定义 BulkRequest 对象BulkRequest request=new BulkRequest();List<Hotel> list = hotelService.list();// 2.查询所有数据并且转换为 List<HotelDoc>List<HotelDoc> listDoc=list.stream().map(t->{HotelDoc doc=new HotelDoc(t);return doc;}).collect(Collectors.toList());// 3.遍历 List<HotelDoc> 对象,创建 IndexRequest 并且通过 add() 添加到 BulkRequest 对象中for (HotelDoc doc: listDoc) {IndexRequest indexRequest=new IndexRequest("hotel").id(doc.getId().toString());indexRequest.source(JSON.toJSONString(doc),XContentType.JSON);request.add(indexRequest);}// 4.发送 ES 请求restHighLevelClient.bulk(request,RequestOptions.DEFAULT);
}

在 kibana 控制台通过查询命令可以查询到已经导入数据

#1.hotel为索引库名称
GET /hotel/_search

四 RestClient查询

RestClient 所有解析的归根结底是逐层解析 Json 结构

1.快速入门(match_all)

通过 match_all 演示 RestClient 的基本API

编写以下测试代码,实现 match_all 的使用

@Test
void testMatchAll() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request=new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(QueryBuilders.matchAllQuery()).from(0).size(3);	//3.发送请求,得到响应结果SearchResponse response=restHighLevelClient.search(request, RequestOptions.DEFAULT);//4. 通过 SearchResponse 对象的 getHits 拿到数据结果集,然后进行处理SearchHits hits = response.getHits();//5.获取数据总条数Long totalHits  = hits.getTotalHits().value;System.out.println(totalHits);//6.处理结果for (SearchHit hit : hits) {HotelDoc doc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);System.out.println(doc.toString());}
}

RestClient 与 DSL 语法对比:

在这里插入图片描述

  • 使用 QueryBuilders 实现各种查询方式
  • 通过 SearchRequest 对象的 source() 对象实现链式编程,从而设置分页,排序,高亮,复合查询等操作
  • 通过 RestHighLevelClient 对象的 search() 方法发送查询请求,实现查询
  • 通过 SearchResponse 对象接受查询结果并进行处理
  • 通过 SearchHits 对象拿到结果集和数据总行数

主要通过解析 DSL 语法返回结果集中的 hits 对象,获取到总条数及数据内容,从而对数据进行处理

2.全文检索

全文检索 与 match_all 的API基本一致,差别在于 match 有了查询条件,即 DSL 语句中的 query 部分。

1.match(特定字段)

//1.创建 SearchRequest 对象,指定索引库名称
SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数
request.source().query(QueryBuilders.matchQuery("all", "如家"));//3.发送请求,得到响应结果
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

2.multi_match(多字段)

//1.创建 SearchRequest 对象,指定索引库名称
SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数
request.source().query(QueryBuilders.multiMatchQuery("如家","name","brand"))//3.发送请求,得到响应结果
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

match 和 multi_match 的区别主要在于 QueryBuilders 类的调用方法不同

match 中 QueryBuilders 使用了 matchQuery()

multi_match 中 QueryBuilders 使用了 multiMatchQuery()

3.term(精准查询)

与 前面的相比,唯一的区别是使用了 termQuery()

@Test
void testMatch() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(QueryBuilders.termQuery("brand","如家"));//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}

3.boolean query(组合)

boolean query 查询 与 前面的查询略有不同,需要先创建 BoolQueryBuilder 对象,然后通过 BoolQueryBuilder 对象添加过滤条件

@Test
void testBooleanQuery() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.创建 BoolQueryBuilder 对象,通过链式编程方式添加查询条件BoolQueryBuilder queryBuilder = new BoolQueryBuilder();queryBuilder.should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300));//3.组织 DSL 参数,填充 BoolQueryBuilder对象到 query中request.source().query(queryBuilder);//4.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}

或者可以直接一步到位

@Test
void testBooleanQuery() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(new BoolQueryBuilder().should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300)));//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}

4.排序和分页

DSL 语句中,排序和分页 在 query 同级,RestClient 中与 DSL 一样,也是在 query() 之后

@Test
void testPageAndOrder() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(new BoolQueryBuilder().should(QueryBuilders.matchQuery("city", "上海")).must(QueryBuilders.termQuery("brand", "如家")).filter(QueryBuilders.rangeQuery("price").gte(200).lte(300))).from(0).size(3).sort("score", SortOrder.DESC).sort("price",SortOrder.ASC);//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}
  • from:开始位置,默认为0
  • size:获取的数据数量,默认为10
  • sort:使用 sort() 方法来进行排序,传入 字段名 和 排序方式

在这里插入图片描述

5.highlight(高亮显示)

高亮API包括 请求DSL构建 和 结果解析 两部分

在这里插入图片描述

测试代码实现如下:

@Test
void testHighLight() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(QueryBuilders.matchQuery("all", "如家")).highlighter(//高亮字段是否和查询字段匹配new HighlightBuilder().field("name").requireFieldMatch(false).preTags("<em>").postTags("</em>"));//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);//4. 通过 SearchResponse 对象的 getHits 拿到数据结果集,然后进行处理SearchHits hits = response.getHits();//5.获取数据总条数Long totalHits = hits.getTotalHits().value;System.out.println(totalHits);for (SearchHit hit : hits) {HotelDoc doc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);// 获取高亮区域的值Map<String, HighlightField> fields = hit.getHighlightFields();if (fields != null) {// 获取指定字段的高亮内容对象HighlightField highlightField = fields.get("name");String highLightName = highlightField.getFragments()[0].toString();// 替换原有的值doc.setName(highLightName);}System.out.println(doc);}
}

6.距离排序

按照距离排序,需要提供一组经纬度作为中心点,然后按照距离远近进行排序

距离排序中使用到一个新的对象,即 SortBuilders,以下是测试示例代码:

@Test
void testDistanceSort() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");//2.组织 DSL 参数request.source().query(QueryBuilders.matchQuery("all", "如家")).sort(SortBuilders.geoDistanceSort("location", new GeoPoint("31.251433,121.47522")).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}

7.相关性算分

使用 RestClient 实现相关性算分时代码较为复杂,先看对照示例:

在这里插入图片描述

实现方式是基于 FunctionScoreQueryBuilder 对象

以下是测试代码,用于过滤 isAD=true 时,增加权重分值为10,且与原始分值相乘

@Test
void testFunctionScore() throws IOException {//1.创建 SearchRequest 对象,指定索引库名称SearchRequest request = new SearchRequest("hotel");// 原始查询,可以是一般查询,也可以是复合查询 BooleanQueryQueryBuilder queryBuilder = QueryBuilders.matchQuery("all", "如家");// 构建 FunctionScoreQueryBuilder 对象FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(//方式查询queryBuilder,//functionScore 数组new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{// 其中一个 function scorenew FunctionScoreQueryBuilder.FilterFunctionBuilder(//过滤条件QueryBuilders.termQuery("isAD", true),//算分函数,这里使用加权重ScoreFunctionBuilders.weightFactorFunction(20))})// 加权模式.boostMode(CombineFunction.MULTIPLY);;//2.组织 DSL 参数request.source().query(QueryBuilders.matchQuery("all", "如家"));//3.发送请求,得到响应结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
}

注意:后边的 “isAD” 字段 是为了做相关性算分增加的新字段,前边的 索引库 和 实体类 中没有,需要手动增加。

本结知识点梳理完成,完结撒花。


http://www.ppmy.cn/news/5087.html

相关文章

m基于CNN卷积神经网络的IBDFE单载波频域均衡算法

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 单载波频域均衡(SC-FDE)是解决符号间干扰(ISI)问题的一项重要技术。相比于单载波时域均衡(SC-TDE)技术和正交频分复用(OFDM)技术,SC-FDE技术具有复杂度低、峰均功率比小的优点。但是,SC-FDE技术中…

【人脸识别】SVM和PCA人脸识别【含GUI Matlab源码 369期】

⛄一、简介 1 PCA-SVM原理 1.1 主成分分析PCA 本文处理的所有原始图片都是112x 92大小的pgm格式图片&#xff0c; 每幅图片包含10304个像素点&#xff0c; 每一行代表一个样本&#xff0c;维数就是10304维。维数过大使得数据处理工作十分复杂&#xff0c;同时&#xff0c;图片…

MySQL基础操作汇总(干货)

数据库操作&#xff1a; 1)创建数据库&#xff1a;create database数据库名; 2)查看所有数据库&#xff1a;show databases; 3)选中指定数据库&#xff1a;use 数据库名; 4)删除数据库&#xff1a; drop database数据库名; 数据表操作 1)创建表&#xff1a;create table表…

ITSS认证流程

ITSS认证流程 ITSS实施的核心是采用建立质量管理体系的PDCA方法论&#xff08;计划-执行-检查-改进&#xff09;实施过程管控&#xff0c;根据ITSS标准的各项要求&#xff0c;对人员、过程、技术和资源四个关键要素进行全面整合&#xff0c;并与IT服务全生命周期的规范化管理相…

Hive DDL 数据定义

文章目录DDL 数据定义一&#xff0c;创建数据库1&#xff09;创建一个数据库&#xff0c;数据库在 HDFS 上的默认存储路径是/user/hive/warehouse/*.db。2&#xff09;避免要创建的数据库已经存在错误&#xff0c;增加 if not exists 判断。&#xff08;标准写法&#xff09;3&…

无形资产评估的9个必要性

必要性一&#xff1a;摸清家底&#xff0c;维护企业资产的完整 有形资产的评估、管理、利用&#xff0c;多年来企业自身已成了一套科学、有效的管理制度&#xff0c;其资产量在相关财务报告中已得到了充分的揭示。但是一个企业到底有哪些无形资产&#xff0c;其价值量如何&…

[附源码]计算机毕业设计Python公益组织登记与查询系统论文(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

大一学生《Web编程基础》HTML实例网页代码 HTML+CSS+JS 黑色横排的个人主页作品

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…