在前面读者朋友们可以了解到ES承载着和MySQL一样的“存储-查询”功能,那么就类似的会有建表语句、表结构、表数据,有了这些才可以存储-查询数据。而这些对应的在ES中是:Mapping映射(表结构-建表语句)、索引库(表本身)、文档(表数据)。本节笔者将带领大家完整上述概念的创建、使用。
1.es的一些概念
elasticsearch中有很多独有的概念,与mysql中略有差别,但也有相似之处。
1.1.文档和字段
elasticsearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中:
"ID": 1,
"TITLE":"小米手机",
"PRICE":3499
ID
TITLE
PRICE
"ID": 2,
"TITLE":"华为手机",
1
3499
小米手机
"PRICE":4999
2
华为手机
4999
"ID":3,
3
华为小米充电器
49
"TITLE":"华为小米充电器",
"PRICE":49
小米手环
299
"ID": 4,
"小米手环"
"TITLE":"
"PRICE": 299
而Json文档中往往包含很多的字段(Field),类似于数据库中的列。
1.2.索引和映射
索引(Index),就是相同类型的文档的集合。例如:
●所有用户文档,就可以组织在一起,称为用户的索引;
●所有商品的文档,可以组织在一起,称为商品的索引;
●所有订单的文档,可以组织在一起,称为订单的索引;
因此,我们可以把索引当做是数据库中的表。
数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。
订单索引
用户索引
商品索引
"ID": 1,
"ID": 101,
"TITLE":"小米手机",
"NAME":"张三",
"ID":10,
"USERID": 101,
"PRICE":3499
'AGE": 21
子
"GOODSID": 1,
"TOTALFEE": 294
}
"ID": 2,
"ID":102,
"华为手机",
"TITLE":
"NAME":"李四",
"PRICE":4999
"AGE": 24
"ID": 11,
了
"USERID":102,
"GOODSID":2,
了
"ID": 3,
"TOTALFEE":328
"ID":
:103
"三星手机",
"TITLE"
"NAME":"麻子",
3999
"PRICE":
AGE":18
1.3.mysql与elasticsearch
我们统一的把mysql与elasticsearch的概念做一下对比:
MySQL |
Elasticsearch |
说明 |
Table |
Index |
索引(index),就是文档的集合,类似数据库的表(table) |
Row |
Document |
文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
Column |
Field |
字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
Schema |
Mapping |
Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
SQL |
DSL |
DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
是不是说,我们学习了elasticsearch就不再需要mysql了呢?并不是如此,两者各自有自己的擅长支出:
● Mysql:擅长事务类型操作,可以确保数据的安全和一致性
● Elasticsearch:擅长海量数据的搜索、分析、计算
因此在企业中,往往是两者结合使用:
●对安全性要求较高的写操作,使用mysql实现
●对查询性能要求较高的搜索需求,使用elasticsearch实现
●两者再基于某种方式,实现数据的同步,保证一致性
搜索
数据同步
CRUD
写操作
MYSQL
1.4.安装es、kibana
1.4.1.安装
参考:链接
1.4.2.分词器
参考:链接
1.4.3.总结
分词器的作用是什么?
●创建倒排索引时对文档分词
●用户搜索时,对输入的内容分词
IK分词器有几种模式?
●ik_smart:智能切分,粗粒度
●ik_max_word:最细切分,细粒度
IK分词器如何拓展词条?如何停用词条?
●利用config目录的IkAnalyzer.cfg.xml文件添加拓展词典和停用词典
●在词典中添加拓展词条或者停用词条
2.索引库操作
索引库就类似数据库表,mapping映射就类似表的结构。我们要向es中存储数据,必须先创建“库”和“表”。
2.1.mapping映射属性
mapping是对索引库中文档的约束,常见的mapping属性包括:
●type:字段数据类型,常见的简单类型有:
○字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
○数值:long、integer、short、byte、double、float、
○布尔:boolean
○日期:date
○对象:object
●index:是否创建索引,默认为true
●analyzer:使用哪种分词器
●properties:该字段的子字段
例如下面的json文档:
对应的每个字段映射(mapping):
●age:类型为 integer;参与搜索,因此需要index为true;无需分词器
●weight:类型为float;参与搜索,因此需要index为true;无需分词器
●isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器
●info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smart
●email:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器
●score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器
●name:类型为object,需要定义多个子属性
○name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器
○name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器
2.2.索引库的CRUD
这里我们统一使用Kibana编写DSL的方式来演示。
2.2.1.创建索引库和映射
基本语法:
●请求方式:PUT
●请求路径:/索引库名,可以自定义
●请求参数:mapping映射
格式:
示例:
2.2.2.查询索引库
基本语法:
● 请求方式:GET
● 请求路径:/索引库名
● 请求参数:无
格式:
示例:
"HEIMA"
234567894
"LASTNAME":{
{子,
'ALIASES"
"TYPE":"KEYWORD"
"MAPPINGS
"PROPERTIES":{
"EMAIL":{
"TYPE":"KEYWORD",
"INDEX":FALSE
INFO":{
10
"TEXT",
"TYPE":
11
#查询
:"IK_SMART"
12
"ANALYZER"
GET/HEIMA
13
14
"NAME
15
"PROPERTIES"
"FIRSTNAME"
16
"KEYWORD"
"TYPE"
17
18
19
LASTNAME'
"KEYWORD"
20
"TYPE":
21
22`
23~
24
25
2.2.3.修改索引库
倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping。虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。
语法说明:
示例:
#修改索引库,添加新字段
"ACKNOWLEDGED": TRUE
2
PUT/HEIMA/_MAPPING
D
3
4
"PROPERTIES":
"AGE":{
"INTEGER"
"TYPE"
2.2.4.删除索引库
语法:
● 请求方式:DELETE
● 请求路径:/索引库名
● 请求参数:无
格式:
在kibana中测试:
2
"ACKNOWLEDGED"
TRUE
34
CLICK TO SEND REQUEST
#删除
DELETE/HEIMA
2.2.5.总结
索引库操作有哪些?
●创建索引库:PUT /索引库名
●查询索引库:GET /索引库名
●删除索引库:DELETE /索引库名
●添加字段:PUT /索引库名/_mapping
3.文档操作
3.1.新增文档
语法:
示例:
响应:
#插入文档
INDEX":"HEIMA"
POST /HEIMA/_DOC/1
E":" DOC",
TYPE"
"1",
ID"
"INFO":"黑马程序员JAVA讲师",
5
VERSION
"EMAIL": "ZY@ITCAST.CN",
6
'CREATED".
RESULT"
"NAME":
7
SHARDS"
I}
"FIRSTNAME":"云",
"TOTAL": 2,
8
"LASTNAME":"赵"
6
"SUCCESSFUL"
"FAILED":0
10
11
12
_SEQ_NO":0.
13
_PRIMARY_TERM":1
14`}
15
针对同一个index,其中version在每次写操作后都会+1(新增、修改、删除)
HELP
SETTINGS
ORY
1234567
#新增文档
_INDEX" : "HEIMAL8384",
POST /HEIMA18384/_DOC/1
_TYPE" : "_DOC",
ID""
"INFO":"18384明天干啥去呀",
当用户新增文档数据时,如果某个字段为空
:1.
VERSION
"EMAIL":"183@QQ.COM",
SEQ_NO":O,
ES的策略是直接不返回,减少网络开销
"NAME
7
PRIMARY_TERM
1,
"FIRSTNAME":"李",
8
PUNO:
TRUE,
"LASTNAME": "致"
9-
子
SOURCE
:"18384明天干啥去呀",
'INFO"
10
L
: "183@QQ.COM",
11
67
12
NAME
/HEIMA18384/_DOC/1
GET
:"李",
"FIRSTNAME"
13
"致"
"LASTNAME"
99123
14
15
16
17
18
HELP
SETTINGS
NAME`:
"FIRSTNAME":"李",
INDEX"
"HEIMA18384",
234
"LASTNAME":"致"
DOC"
TYPE
ID""
一旦用户存储进去,就会全量返回
5
VERSION" : 1,
6
SEG_NO":1,
POST /HEIMA18384/_DOC/2
_PRIMARY_TERM" : 1,
7
'FOUND": TRUE,
8
"INFO":"18384明天干啥去呀",
子
SOURCE'
"EMAIL"."183@QQ.COM",
"INFO":"18384明天干啥去呀",
10
"AGE":200,
11
"EMAIL":"183@QQ.COM",
"NAME":{
12
"AGE":200,
"FIRSTNAME":"李",
13
NAME
"LASTNAME":"致"
"FIRSTNAME" :"李",
14
"LASTNAME" :"致"
15
了
16-
了
17
#查询文档
18
GET /HEIMA18384/_DOC/2
19
3.2.查询文档
根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把文档id带上。
语法:
通过kibana查看数据:
查看结果:
123
#查询文档
INDEX":"HEIMA",
GET/HEIMA/_DOC/1
_DOC",
TYPE":
3456
ID":"1",
VERSION":1,
SEQ_NO":0,
7
_PRIMARY_TERM":1
8
'FOUND"
TRUE,
6
SOURCE
":"黑马程序员JAVA讲师",
"INFO"
10
: "ZY@ITCAST.CN",
"EMAIL"
11
"NAME":
12
"FIRSTNAME":"云",
13
"LASTNAME":"赵"
14
子
15
工
16
17
18
3.3.删除文档
删除使用DELETE请求,同样,需要根据id进行删除:
语法:
示例:
结果:
#删除文档
INDEX'"
"HEIMA",
DELETE /HEIMA/_DOC/1
3456789
'DOC"
TYPE
"1"
ID"
VERSION"
"DELETED",
'RESULT"
SHARDS
"TOTAL'
60
"SUCCESSFUL":1,
"FAILED":O
10
11
了,
SEQ_NO":1
12
"
13
PRIMARY TERM":1
14`}
3.4.修改文档
修改有两种方式:
●全量修改:直接覆盖原来的文档
●增量修改:修改文档中的部分字段
3.4.1.全量修改
全量修改是覆盖原来的文档,其本质是:
●根据指定的id删除文档
●新增一个相同id的文档
注意:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。
语法:
示例:
3.4.2.增量修改
增量修改是只修改指定id匹配的文档中的部分字段。
语法:
示例:
3.5.总结
文档操作有哪些?
●创建文档:POST /{索引库名}/_doc/文档id { json文档 }
●查询文档:GET /{索引库名}/_doc/文档id
●删除文档:DELETE /{索引库名}/_doc/文档id
●修改文档:
○全量修改:PUT /{索引库名}/_doc/文档id { json文档 }
○增量修改:POST /{索引库名}/_update/文档id { "doc": {字段}}
4.RestAPI
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
其中的Java Rest Client又包括两种:
●Java Low Level Rest Client
●Java High Level Rest Client
我们学习的是Java HighLevel Rest Client客户端API
4.0.导入Demo工程
4.0.1.导入数据
首先导入提供的数据库数据:
tb_hotel.sql(62 KB)
数据结构如下:
4.0.2.导入项目
然后导入提供的项目:
hotel-demo.zip(132 KB)
项目结构如图:
HOTEL-DEMOD://CODE\HOTEL-DEMO
SERVER:
工
.IDEA
8089
PORT:
SRC
SPRING:
MAIN
DATASOURCE:
JAVA
5678
URL:JDBC:MYSQL://MYSQL:3306/HEIMA?USESSL-FALSE
CN.ITCAST.HOTEL
USERNAME:ROOT
MAPPER
PASSWORD:123
POJO
DRIVER-CLASS-NAME: COM.MYSQL.JDBC.DRIVER
SERVICE
LOGGING:
HOTELDEMOAPPLICATION
9
LEVEL:
RESOURCES
10
STATIC
CN.ITCAST: DEBUG
11
TEMPLATES
12
PATTERN:
APPLICATIONYAML
DATEFORMAT: MM-DD HH:MM:SS:SSS
13
TEST
14
MYBATISPLUS:
POM.XML
15
CONFIGURATION:
EXTERNAL LIBRARIES
MAP-UNDERSCORE-TO-CAMEL-CASE: TRUE
16
SCRATCHES AND CONSOLES
TYPE-ALIASES-PACKAGE: CN.ITCAST.HOTEL.POJO
17
4.0.3.mapping映射分析
创建索引库,最关键的是mapping映射,而mapping映射要考虑的信息包括:
●字段名
●字段数据类型
●是否参与搜索
●是否需要分词
●如果分词,分词器是什么?
其中:
●字段名、字段数据类型,可以参考数据表结构的名称和类型
●是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索
●是否分词呢要看内容,内容如果是一个整体就无需分词,反之则要分词
●分词器,我们可以统一使用ik_max_word
来看下酒店数据的索引库结构:
几个特殊字段说明:
●location:地理坐标,里面包含精度、纬度
●all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索
地理坐标说明:
小提示
ES中支持两种地理坐标数据类型:
GEO_POINT:由结度(LATITUDE)和经度(LONGITUDE)确定的一个点.例如:"32.8752345,120.2981576"
GEO.SHAPE;有多个GEO.POINT组成的复杂几何图形.例如一条直线,"UNESTRING(77.036533333333339)"
copy_to说明:
小提示
字段拷贝可以使用COPY_TO属性将当前字段拷贝到指定字段.示例:
"ALL": {
"TYPE": "TEXT",
"ANALYZER": "IK_MAX_WORD"
子.
"BRAND":{
"TYPE":"KEYWORD".
"COPY_TO": "ALL"
4.0.4.初始化RestClient
方式一
在elasticsearch提供的API中,与elasticsearch一切交互都封装在一个名为RestHighLevelClient的类中,必须先完成这个对象的初始化,建立与elasticsearch的连接。
分为三步:
1)引入es的RestHighLevelClient依赖:
2)因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:
3)启动类初始化RestHighLevelClient,初始化的代码如下:
(MAPPERSCAN("CN.ITCAST.HOTEL.MAPPER")
@SPRINGBOOTAPPLICATION
PUBLIC CLASS HOTELDEMOAPPLICATION I
PUBLIC STATIC VOID MAIN(STRING[] ARGS) ( SPRINGAPPLICATION.RUN(HOTELDEMOAPPLICATION.CLASS, ANGS);]]
ABEAN
PUBLIC RESTHIGHLEVELCLIENT INITCLIENT() {
RESTHIGHLEVELCLIENT(RESTCLIENT.BUILDER(
RETURN NEW R
HTTPHOST.CREATE("HTTP://192.168.150.101:9200")
));
4)引入
方式二
可以直接在单测类完整创建、初始化
4.1.创建索引库
4.1.1.代码解读
创建索引库的API如下:
请求路径,索引库名称
PUT /HOTEL
"MAPPINGS":
创建索引库代码如下:
"PROPERTIES":
]D": :
"TYPE": "KEYWORD"
'NAME":
@TEST
"TYPE": "TEXT",
VOID TESTCREATEHOTELINDEX() THROWS IOEXCEPTION {
"ANALYZER": "IK MAX WORD"
//1.创建REQUEST对象
WBDRESS":
CREATEINDEXREQUEST REQUEST - NEW CREATEINDEXREQUEST("HOTEL');
PRICE"
2.请求参数,MAPPING_TEMPLATE是静态常量字符串,内容是创建索引库的DSI语句
DSL
"BRAND":
"CITY":
REQUEST.SOURCE(MAPPING_TEMPLATE,XCONTENTTYPE.JSON);
"STARNAME"
1/3.发起请求
"BUSINESS"
"LOCATION":
.CREATE(REQUEST,REQUESTOPTIONS.DEFAULT);
INDICES()
CLIENT
"TYPE":
GEO_POINT"
"PIC":
"TYPE": "KEYWORD",
返回的对象中包含
"INDEX":FALSE
索引库操作的所有方法
代码分为三步:
●1)创建Request对象。因为是创建索引库的操作,因此Request是CreateIndexRequest。
●2)添加请求参数,其实就是DSL的JSON参数部分。因为json字符串很长,这里是定义了静态字符串常量MAPPING_TEMPLATE,让代码看起来更加优雅。
●3)发送请求,client.indices()方法的返回值是IndicesClient类型,封装了所有与索引库操作有关的方法。
4.1.2.完整示例
在hotel-demo的cn.itcast.hotel.constants包下,创建一个类,定义mapping映射的JSON字符串常量:
在hotel-demo中的HotelIndexTest测试类中,编写单元测试,实现创建索引:
4.2.删除索引库
删除索引库的DSL语句非常简单:
与创建索引库相比:
●请求方式从PUT变为DELTE
●请求路径不变
●无请求参数
所以代码的差异,注意体现在Request对象上。依然是三步走:
●1)创建Request对象。这次是DeleteIndexRequest对象
●2)准备参数。这里是无参
●3)发送请求。改用delete方法
在hotel-demo中的HotelIndexTest测试类中,编写单元测试,实现删除索引:
4.3.判断索引库是否存在
判断索引库是否存在,本质就是查询,对应的DSL是:
因此与删除的Java代码流程是类似的。依然是三步走:
●1)创建Request对象。这次是GetIndexRequest对象
●2)准备参数。这里是无参
●3)发送请求。改用exists方法
4.4.总结
JavaRestClient操作elasticsearch的流程基本类似。核心是client.indices()方法来获取索引库的操作对象。
索引库操作的基本步骤:
●初始化RestHighLevelClient
●创建XxxIndexRequest。XXX是Create、Get、Delete
●准备DSL( Create时需要,其它是无参)
●发送请求。调用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete
5.RestClient操作文档
为了与索引库操作分离,我们再次参加一个测试类,做两件事情:
●初始化RestHighLevelClient
●我们的酒店数据在数据库,需要利用IHotelService去查询,所以注入这个接口
5.1.新增文档
我们要将数据库的酒店数据查询出来,写入elasticsearch中。
5.1.1.索引库实体类
数据库查询后的结果是一个Hotel类型的对象。结构如下:
与我们的索引库结构存在差异:
●longitude和latitude需要合并为location
因此,我们需要定义一个新的类型,与索引库结构吻合:
5.1.2.语法说明
新增文档的DSL语句如下:
对应的java代码如图
ATEST
VOID TESTINDEXDOCUMENT() THROWS IOEXCEPTION {
索引库名,文档ID
//1.创建REQUEST对象
POST /INDEXNAME/_DOC/1
INDEXREQUEST REQUEST - NEW INDEXREQUEST("INDEXNAME').ID("1');
//2.准备JSON文档
"NAME": "JACK",
: 21J", XCONTENTTYPE.JSON);
REQUEST.SOURCE("\\"NAME\": \"JACK\",
'AGE": 21
JSON文档
"AGE
1/3.发送请求
CLIENT.INDEX(REQUEST, REQUESTOPTIONS.DEFAULT);
可以看到与创建索引库类似,同样是三步走:
●1)创建Request对象
●2)准备请求参数,也就是DSL中的JSON文档
●3)发送请求
变化的地方在于,这里直接使用client.xxx()的API,不再需要client.indices()了。
5.1.3.完整代码
我们导入酒店数据,基本流程一致,但是需要考虑几点变化:
●酒店数据来自于数据库,我们需要先查询出来,得到hotel对象
●hotel对象需要转为HotelDoc对象
●HotelDoc需要序列化为json格式
因此,代码整体步骤如下:
●1)根据id查询酒店数据Hotel
●2)将Hotel封装为HotelDoc
●3)将HotelDoc序列化为JSON
●4)创建IndexRequest,指定索引库名和id
●5)准备请求参数,也就是JSON文档
●6)发送请求
在hotel-demo的HotelDocumentTest测试类中,编写单元测试:
5.2.查询文档
5.2.1.语法说明
查询的DSL语句如下:
非常简单,因此代码大概分两步:
●准备Request对象
●发送请求
不过查询的目的是得到结果,解析为HotelDoc,因此难点是结果的解析。完整代码如下:
@TEST
/INDEXNAME/ DOC/1
GET
VOID TESTGETDOCUMENTBYID() THROWS IOEXCEPTION {
1/1.创建REQUEST对象
: NEW GETREQUEST("INDEXNAME", "1");
GETREQUEST REQUEST
1/2.发送请求,得到结果
_INDEX" : "USERS",
_DOC",
GETRESPONSE RESPONSE - CLIENT.GET(REQUEST, REQUESTOPTIONS.DEFAULT);
TYPE
ID''
1/3.解析结果
_VERSION":1
STRING JSON - RESPONSE.GETSOURCEASSTRING();
LSEQ_NO,.:0,
_PRIMARY_TERM": 1,
SYSTEM.OUT.PRINTLN(JSON);
TRUE
FOUND
SOURCE
"JACK",
NAME
":21
AGE"
可以看到,结果是一个JSON,其中文档放在一个_source属性中,因此解析就是拿到_source,反序列化为Java对象即可。
与之前类似,也是三步走:
●1)准备Request对象。这次是查询,所以是GetRequest
●2)发送请求,得到结果。因为是查询,这里调用client.get()方法
●3)解析结果,就是对JSON做反序列化
5.2.2.完整代码
在hotel-demo的HotelDocumentTest测试类中,编写单元测试:
5.3.删除文档
删除的DSL为是这样的:
与查询相比,仅仅是请求方式从DELETE变成GET,可以想象Java代码应该依然是三步走:
●1)准备Request对象,因为是删除,这次是DeleteRequest对象。要指定索引库名和id
●2)准备参数,无参
●3)发送请求。因为是删除,所以是client.delete()方法
在hotel-demo的HotelDocumentTest测试类中,编写单元测试:
5.4.修改文档
5.4.1.语法说明
修改我们讲过两种方式:
●全量修改:本质是先根据id删除,再新增
●增量修改:修改文档中的指定字段值
在RestClient的API中,全量修改与新增的API完全一致,判断依据是ID:
●如果新增时,ID已经存在,则修改
●如果新增时,ID不存在,则新增
这里不再赘述,我们主要关注增量修改。
代码示例如图:
ATEST
VOID TESTUPDATEDOCUMENTBYID() THROWS IOEXCEPTION ;
//1.创建REQUEST对象
EW UPDATEREQUEST("INDEXNAME", "1");
UPDATEREQUEST REQUEST - NEW U
/USERS/_UPDATE/1
1/2.准备参数,每2个参数为一对
KEY
VALUE
索引库名,
文档ID
"DOC":
REQUEST.DOC(
"NAME": "ROSE",
"AGE",18,
"AGE":18
"NAME", "ROSE"
要修改的字段
);
1/3.更新文档
CLIENT.UPDATE(REQUEST, REQUESTOPTIONS.DEFAULT);
与之前类似,也是三步走:
●1)准备Request对象。这次是修改,所以是UpdateRequest
●2)准备参数。也就是JSON文档,里面包含要修改的字段
●3)更新文档。这里调用client.update()方法
5.4.2.完整代码
在hotel-demo的HotelDocumentTest测试类中,编写单元测试:
5.5.批量导入文档
案例需求:利用BulkRequest批量将数据库数据导入到索引库中。
步骤如下:
● 利用mybatis-plus查询酒店数据
● 将查询到的酒店数据(Hotel)转换为文档类型数据(HotelDoc)
● 利用JavaRestClient中的BulkRequest批处理,实现批量新增文档
5.5.1.语法说明
批量处理BulkRequest,其本质就是将多个普通的CRUD请求组合在一起发送。其中提供了一个add方法,用来添加其他请求:
BULKREQUEST.JAVA
ANONYMOUS CLASSES(CTRL++1)
LAMBDAS(CTRL+L)
INHERITED MEMBERS(CTRL+F12)
ADD(DELETEREQUEST):BULKREQUEST
ADD(DOCWRITEREQUEST<?>):BULKREQUEST
ADD(DOCWRITEREQUEST<?>....):BULKREQUEST
ADD(INDEXREQUEST):BULKREQUEST
ADD(ITERABLE<DOCWRITEREQUEST<?>>):BULKREQUEST
ADD(UPDATEREQUEST):BULKREQUEST
APPLYGLOBALMANDATORYPARAMETERS(DOCWRITEREQUEST<?>):VOID
可以看到,能添加的请求包括:
●IndexRequest,也就是新增
●UpdateRequest,也就是修改
●DeleteRequest,也就是删除
因此Bulk中添加了多个IndexRequest,就是批量新增功能了。示例:
@TEST
IOEXCEPTION {
VOID
THROWS
TESTBULK()
1.创建BULK请求
BULKREQUEST REQUEST ; NEW BULKREQUEST();
2.添加要批量提交的请求:这里添加了两个新增文档的请求
REQUEST.ADD(NEW INDEXREQUEST("HOTEL")
.ID("101").SOURCE("JSON SOURCE", XCONTENTTYPE.JSON);
REQUEST.ADD(NEW INDEXREQUEST("HOTEL")
.ID("102").SOURCE("JSON SOURCE2",
XCONTENTTYPE.JSON));
1/3.发起BULK请求
CLIENT.BULK(REQUEST, REQUESTOPTIONS.DEFAULT);
其实还是三步走:
●1)创建Request对象。这里是BulkRequest
●2)准备参数。批处理的参数,就是其它Request对象,这里就是多个IndexRequest
●3)发起请求。这里是批处理,调用的方法为client.bulk()方法
我们在导入酒店数据时,将上述代码改造成for循环处理即可。
5.5.2.完整代码
在hotel-demo的HotelDocumentTest测试类中,编写单元测试:
Java
运行代码复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
void testBulkRequest() throws IOException {
// 批量查询酒店数据
List<Hotel> hotels = hotelService.list();
// 1.创建Request
BulkRequest request = new BulkRequest();
// 2.准备参数,添加多个新增的Request
for (Hotel hotel : hotels) {
// 2.1.转换为文档类型HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 2.2.创建新增文档的Request对象
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
通过指令查询:GET /hotel/_search
SETTINGS HELP
"PIC":
"TOOK" : 1023,
"TYPE": "KEYWORD",
"TIMED_OUT" : FALSE,
"INDEX":FALSE
SHARDS"
"TOTAL" : 1,
'ALL": {
"SUCCESSFUL" : 1,
"TYPE":"TEXT",
"SKIPPED":0,
"ANALYZER": "IK MAX WORD"
8
"FAILED":O
9
总数据量应该跟数据库保持一致
ITE
10
"TOTAL":{
11
'VALUE"
12
200
13
"RELATION
"EG
#查询索引库
24
GET /HOTEL18483
1.0.
"MAX_SCORE
"HITS" : [
16
#查询指定ID文档的数据
17
GET /HOTEL18483/_DOC/36934
: "HOTEL18483",
18
INDEX":
_TYPE": "_DOC",
19
GET /HOTEL18483/_SEARCH
ID":"36934",
20
21
SCORE":1.0.
22
SOURCE
"静安交通路40号",
3元
LADDRESS
"7天酒店"
"BRAND""
5.6.小结
文档操作的基本步骤:
●初始化RestHighLevelClient
●创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk
●准备参数(Index、Update、Bulk时需要)
●发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
●解析结果(Get时需要)
若有收获,就点个赞吧