01、课程目标

  • 能够说出ElasticSearch的作用
  • 能够安装ElasticSearch服务
  • 能够说出ElasticSearch的相关概念
  • 能够使用Postman、Kibana发送Restful请求操作ElasticSearch
  • 能够说出分词器的作用
  • 能够使用ElasticSearch集成IK分词器
  • 掌握HighLevelRestClient如何操作ES

02、初识ElasticSearch-倒排索引

  1. - 注意一个点:ElasticSearch是用于构建索引库便于实现海量数据的复杂查询;

1)数据库存在的问题

目标:能够说出数据库查询存在的问题

image.png

问题一:查询title中包含 “手机” 的信息?

SELECT * FROM goods WHERE title LIKE '%手机%';

结论:如果使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低

问题二:查询title中包含‘我要买一部华为手机’的信息?

SELECT * FROM goods WHERE title LIKE ‘%我要买一部华为手机%';

结论:关系型数据库提供的查询,功能太弱

2)* 倒排索引

目标:了解倒排索引的存储原理

使用新华字典查找汉字,先找到汉字的偏旁部首,再根据偏旁部首对应的目录(索引)找到目标汉字。

ES样例:

  • 文档0(编号0): we like java java java
  • 文档1(编号1): we like lucene lucene lucene

建立倒排索引的流程:
1)首先对所有数据的内容进行拆分(分词),拆分成唯一的一个个词语(词条Term)
2)然后建立词条和每条数据的对应关系(词条在文档出现的位置下标,出现频率)

(Term 词条) (Doc ID,Freq 频率) (Pos 位置)
we (0,1) (1,1) (0,0)(1,0)
like (0,1) (1,1) (0,1)(1,1)
java (0,3) (2,3,4)
lucene (1,3) (2,3,4)

小结

倒排索引:将每条数据中的内容进行分词,形成词条。然后记录词条和数据的唯一标识(id)的对应关系,形成的产物。

03、初识ElasticSearch-ElasticSearch存储和搜索原理

目标:能够说出ElasticSearch存储和搜索原理

假设数据库存在以下几条数据:

image.png

ES中存储及搜索原理图

image.png
image.png

说明:ES的数据库我们称之为 index(索引库),每条数据我们称之为 document(文档),ES在存储文档的时候,会对它需要分词的字段内容进行切分,切分成一个个词条,再建立每个词条与文档唯一标识(id)的对应关系,即倒排索引。

我们再回过头看之前数据库存在的两个问题,通过ES是否能够解决:

  • 性能低:使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低
    ES解决方案:如果使用”手机”作为关键字查询,ES生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度。
  • 功能弱:如果以”华为手机” 作为条件,查询不出来数据
    ES解决方案:如果使用”华为手机”作为关键字查询,ES也可以对搜索的关键字进行分词,比如将华为手机拆分成”华为”、”手机”,然后根据两个词分词去倒排索引中进行查询,然后取结果的并集。

04、初识ElasticSearch-ElasticSearch概念

目标:能够说出ElasticSearch和数据库的区别以及分工

1)介绍

ElasticSearch是Java语言开发的,并作为Apache许可条款下的开放源码发布,基于Lucene实现,是一款分布式、高扩展、近实时的搜索服务,可以基于RESTful web接口进行操作。官网:https://www.elastic.co/

基于Lucene的产品:

1)Solr:实时性偏弱,在高并发地写入数据时,Slor需要频繁地构建索引库,而索引库构建影响到查询性能

2)Elasticsearch:实时性非常强(近实时),ES在频繁地构建索引库的同时,不太影响查询的性能

2)应用场景

  • 海量数据的查询(京东,淘宝商品搜索)
  • 日志数据分析 (ELK ELasticsearch + Logstach + Kibna 搭建日志监控平台)
  • 实时数据分析
    image.png

3)Elasticsearch vs MySQL

我们统一的把mysql与elasticsearch的概念做一下对比:

MySQL Elasticsearch 说明
Table Index 索引(index),就是文档的集合,类似数据库的表(table)
Row Document 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
Column Field 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)
Schema Mapping Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQL DSL(Domain Specification Lanuage) DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD

是不是说,我们学习了elasticsearch就不再需要mysql了呢?

并不是如此,两者各自有自己的擅长支出:

  • Mysql:擅长事务类型操作,可以确保数据的安全和一致性,进行复杂的多表查询
  • Elasticsearch:擅长海量数据的搜索、分析、计算

因此在企业中,往往是两者结合使用:

  • 对安全性要求较高的写操作,关系复杂的表需求使用mysql实现
  • 对查询性能要求较高的搜索需求,使用elasticsearch实现
  • 两者再基于某种方式,实现数据的同步,保证一致性

image.png

4)小结

image.png

05、安装ElasticSearch-安装Elasticsearch

虚拟机账户CentOS7:

root

itcast

没有ifcofnig命令,只安装ip addr,输入ip addr查看IP地址

虚拟机已经设置静态IP,IP地址为192.168.66.133

方法一:修改VMware虚拟机软件的VMnet8网卡

image.png

image.png

方法二:到虚拟机里面修改/etc/sysconfig/network-scripts/ifcfg-ens33 文件

创建Elasticsearch容器:

这里使用阿里云服务进行搭建;

docker run -id --name elasticsearch -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" -d -p 9200:9200 -v /usr/share/elasticsearch/plugins:/usr/share/elasticsearch/plugins -e "discovery.type=single-node" elasticsearch:7.4.0

-e :设置运行内存,当启动成功但无法访问页面时设置; -v /usr/share/elasticsearch/plugins:/usr/share/elasticsearch/plugins:设置插件安装路径,在后续可以添加插件到这里;如:IK分词器插件;

  • 在8.x版本中,新增了一个访问安全组配置,默认是不允许外部端口进行访问(开启SSL认证);若想用外部端口进行访问ES需要:
    • 修改elasticsearch.yml配置文件
    • xpack.security.enabled设置为false

9200端口:http端口

http://119.23.63.60:9200

image.png

06、安装ElasticSearch-安装Kibana

为了更方面编写搜索代码,我们先在Kibana中写出DSL语句

创建Kibana容器

docker run -d -p 5601:5601 --link elasticsearch --name=kibana -e "ELASTICSEARCH_URL=http://119.23.63.60:9200" kibana:7.4.0

容器启动成功后,浏览器输入:http://119.23.63.60/:5601/

7.ximage.png8.ximage.png

07、DSL操作ES-RESTful风格介绍

REST(Representational State Transfer 表述性状态转移),是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是RESTful。就是一种定义接口的规范。有以下特征:

  • 基于HTTP
  • 可以使用XML格式定义或JSON格式定义参数和返回值。
  • 每一个URI代表1种资源。
  • 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:
    • GET:用来获取资源
    • POST:用来新建资源(也可以用于更新资源)
    • PUT:用来新建资源
    • DELETE:用来删除资源

如果有一个/user资源

get: /user/1   ---查询
delete: /user/1   ---删除
post: /user  ---添加、修改
put: /user  ---添加、修改
  • get: /user/1 —-查询
  • delete: /user/1 —-删除
  • post: /user —-添加、修改
  • put: /user —-添加、修改

08、DSL操作ES-操作索引库

image.png

目标:能够使用postMan对索引库进行操作

  • 添加索引
PUT goods_index

image.png

  • 查询索引
GET goods_index

image.png

  • 删除索引
DELETE goods_index

image.png

  • 关闭索引
POST goods_index/_close

image.png

说明:当索引进入关闭状态,是不能添加文档的

  • 打开索引
POST goods_index/_open

image.png

# 创建索引库
PUT goods

# 查询索引库
GET goods

# 删除索引库
DELETE goods

# 临时关闭索引库,一旦关闭索引库,无法操作文档
POST goods/_close

# 打开索引库
POST goods/_open

09、DSL操作ES-操作映射

1)* 数据类型

目标:了解ES中字段有哪些数据类型

image.png

ES中包含两类数据类型:简单数据类型和复杂数据类型

1)简单数据类型

  • 字符串

    • text:可以分词,不支持聚合(统计)
    • keyword:不会分词,将全部内容作为一个词条,支持聚合(统计)
      例如:有个文档(相当于数据库一条数据),其中一个字段的值是华为手机
      text: 华为、手机
      keyword: 华为手机
      

      例如:SQL数据库中的主键映射也用: keyword;因为在ES中的_id是一个字符串;且不应该被分词; 因此,有 text 类型的就一定会分词; 在实际开发中,要好好考虑字段是否需要分词;

  • 数值类型 一定不分词
    image.png

  • 布尔(boolean)
  • 二进制(binary)
  • 范围类型(integer_range, float_range, long_range, double_range, date_range)(了解即可)
  • 日期(date)

    注意:所有字段默认会设置index类型为true;而只有设置index为true的字段才能被搜索; 因此,不需要被搜索的字段需要设置index为FALSE;在实际开发中,要好好考虑字段是否能被搜索到;

2)复杂数据类型

  • 数组 []:没有专用的array数据类型,任何一个字段的值,都可以被添加0个到多个,但要求他们的类型必须一致,当类型一直含有多个值存储到ES中会自动转化成数组类型 [“eric”,”jack”] List Set
  • 对象 {} Map User {key:value,key:value}
    image.png

2)操作映射

  • 添加映射
PUT person
{
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      },
      "age":{
        "type": "integer"
      }
    }
  }

}
  • 查询映射
GET person/_mapping

image.png

  • 添加字段
PUT person/_mapping
{
  "properties":{
    "sex":{
      "type":"keyword"
    }
  }
}

image.png

再次查询映射

image.png

# 注意:ES不能单独修改映射字段名称或类型,不能单独删除某个字段,如果需要修改,直接删除整个索引库再重建
# 创建索引库并设置映射信息
PUT person
{
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      },
      "age":{
        "type": "integer"
      }
    }
  }
}

# 查询映射信息
GET person/_mapping


# 往映射添加字段
PUT person/_mapping
{
   "properties": {
      "sex":{
        "type": "keyword"
      }
    }
}

注意:在ES中,一旦索引库的映射构建完成,将来就不能对索引库进行修改或删除,如果要进行修改,只能进行重构;重构索引库后需要进行全量同步索引库(非业务高峰期发起同步)

10、* DSL操作ES-操作文档

目标:能够在kibana中操作文档

  • 添加文档,指定id

    在8.x版本后就不需要指定类型了

POST person/_doc/1
{
  "name":"张三",
  "age":18,
  "sex":"男"
}

_doc : 是指定的类型/分类(或者说是文档的子索引);

image.png

  • 添加文档,不指定id

    POST person/_doc
    {
    "name":"翠花",
    "age":20,
    "sex":"女"
    }
    

    image.png

  • 版本的文档类型差异:

image.png

在8.0以上就不需要设置类型了;但是,在8.x版本中如果定义了类型,那么在后续的操作也要加上类型;如,GET查询,否则会报错

  • 查询文档
GET person/_doc/1    # 查询一个
  • 查询文档是根据 : _ id 来进行查询的;
  • 在实际开发中,这个 _id 值都是我们自定义的;一般都是将MySQL中的主键值设置为 _id 值;

    image.png

GET person/_search   # 查询全部

image.png

  • 修改文档
PUT person/_doc/1
{
  "name":"如花",
  "age":20,
  "sex":"女"
}

image.png

  • 删除文档
DELETE person/_doc/1

image.png


# 新增文档
# _doc: 是索引的类型(type)名称
# 在Elasticsearch6.x版本或之前时,类型可以自定义的
# 在Elasticsearch7.x版本时,提供默认类型_doc,不能修改的

# 自动分配主键值
POST person/_doc
{
  "name":"翠花",
  "age":18,
  "sex":"女"
}

# 自行设置主键值
POST person/_doc/2
{
  "name":"如花",
  "age":28,
  "sex":"男"
}

# 根据id查询一个文档
# _id: 是Elasticsearch的主键字段(唯一的)

GET person/_doc/2

# 查询所有文档
GET person/_search

# 修改文档
PUT person/_doc/2
{
  "name":"如花",
  "age":30,
  "sex":"男"
}

# 删除文档
DELETE person/_doc/2

注意:在实际开发中文档id都应该要我们来进行自定义,一般id值对应的是SQL数据库的主键;方便在后续进行查询;

11、▲ 分词器-分词器介绍

目标:了解什么是分词器

分词器(Analyzer)是将一段文本,按照一定逻辑,拆分成多个词语的一种工具,如:华为手机  --->  华为、手、手机,**ElasticSearch 内置分词器有以下几种**:
  • Standard Analyzer - 默认分词器,按词/字切分,小写处理 (英文)
  • Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
  • Stop Analyzer - 小写处理,停用词过滤(the,a,is)
  • Whitespace Analyzer - 按照空格切分,不转小写
  • Keyword Analyzer - 不分词,直接将输入当作输出
  • Patter Analyzer - 正则表达式,默认\W+(非字符分割) (中文会被去掉)
  • Language - 提供了30多种常见语言的分词器

ES提供了一个接口给我们来验证分词效果,如下所示:

# 分词效果验证
GET _analyze
{
  "text": "我爱黑马程序员",
  "analyzer": "standard"
}

分词结果:

image.png

  • ElasticSearch 内置分词器对中文很不友好,处理方式为:一个字一个词。比如,在查询人名的时候会导致找不到;
    • 因为,将人名也分开来了,系统找不到完整的人名;


  • 因此,我们利用国内开发的分词器;

12、 ▲ 分词器-IK分词器介绍及安装

目标:学会如何使用集成ik分词器

IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包,是一个基于Maven构建的项目,具有60万字/秒的高速处理能力,并且支持用户词典扩展定义。下载地址:[https://github.com/medcl/elasticsearch-analysis-ik/releases](https://github.com/medcl/elasticsearch-analysis-ik/releases)

   还有庖丁解牛分词器,但是更新缓慢,被舍弃;

分词器的核心:

注意:版本要和ES版本对应,否则会导致ES启动失败;

1)词库

2)分词算法

  • ik_smart:最小分词法 我是程序员 -> 我 是 程序员
  • ik_max_word:最细分词法 我是程序员 -> 我 是 程序员 程序 员

登录Elasticsearch容器

docker exec -it elasticsearch /bin/bash

上传ik分词器目录到linux(放在/root目录下)

image.png

运行以下命令拷贝文件到ES

docker cp analysis-ik elasticsearch:/usr/share/elasticsearch/plugins

重启ES容器

docker restart elasticsearch

验证ik分词器是否安装成功
analyzer:用于设置分词器算法;

GET _analyze
{
  "text": "我爱黑马程序员",
  "analyzer": "ik_max_word"
  //"analyzer": "standard"
}

image.png

Ik分词器的算法:

1)(常用) ik_smart:最小分词。如:我是程序员,会分为 我 是 程序员 三个词

2)ik_max_word:最细粒度分词。如:我是程序员 会分为 我 是 程序员 程序 员 5个词

13、分词器-使用IK分词器

1)没有设置分ik词器之前

目前person索引库的name字段为text类型,我们使用term查询(不分词查询)发现并没有结果!

GET person/_search
{
  "query": {
    "term": {
      "name": {
        "value": "翠花"
      }
    }
  }
}

因为默认情况下,text类型使用standard(单字)分词器进行分词。

2)重建索引并设置ik分词器

这时需要删除person索引库,然后重新设置name的类型为text,且analyzer为ik_max_word

PUT person
{
  "mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "age":{
        "type": "integer"
      }
    }
  }

}

插入数据

POST person/_doc/1
{
  "name":"翠花",
  "age":20,
  "sex":"女"
}

再次查询

GET person/_search
{
  "query": {
    "term": {
      "name": {
        "value": "翠花"
      }
    }
  }
}

这时就有结果了

image.png

3)* IK分词器-扩展词词典

随着互联网的发展,“造词运动”也越发的频繁。出现了很多新的词语,在原有的词汇列表中并不存在。比如:“奥力给”,“传智播客” 等。

所以我们的词汇也需要不断的更新,IK分词器提供了扩展词汇的功能。

1)打开IK分词器config目录:

image.png

2)在IKAnalyzer.cfg.xml配置文件内容添加:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 *** 添加扩展词典-->
        <entry key="ext_dict">ext.dic</entry>
</properties>

3)新建一个 ext.dic,可以参考config目录下复制一个配置文件进行修改

传智播客
奥力给

4)重启elasticsearch

docker restart es

# 查看 日志
docker logs -f elasticsearch

image.png

日志中已经成功加载ext.dic配置文件

5)测试效果:

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "传智播客Java就业超过90%,奥力给!"
}

注意当前文件的编码必须是 UTF-8 格式,严禁使用Windows记事本编辑

4)* IK分词器-停用词词典

在互联网项目中,在网络间传输的速度很快,所以很多语言是不允许在网络上传递的,如:关于宗教、政治等敏感词语,那么我们在搜索时也应该忽略当前词汇。

IK分词器也提供了强大的停用词功能,让我们在索引时就直接忽略当前的停用词汇表中的内容。

1)IKAnalyzer.cfg.xml配置文件内容添加:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典-->
        <entry key="ext_dict">ext.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典  *** 添加停用词词典-->
        <entry key="ext_stopwords">stopword.dic</entry>
</properties>

3)在 stopword.dic 添加停用词

就业

4)重启elasticsearch

# 重启服务
docker restart elasticsearch
docker restart kibana

# 查看 日志
docker logs -f elasticsearch

日志中已经成功加载stopword.dic配置文件

5)测试效果:

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "传智播客Java就业率超过95%,奥力给!"
}
  • 分词的结果中就没有了“就业”的词;

    注意当前文件的编码必须是 UTF-8 格式,严禁使用Windows记事本编辑

14、* HighLevelAPI-RestAPI介绍&项目导入

1)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

image.png

我们学习的是Java HighLevel Rest Client客户端API

2)项目导入

首先导入课前资料提供的数据库数据:

数据结构如下:

CREATE TABLE `tb_hotel` (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) NOT NULL COMMENT '酒店名称',
  `address` varchar(255) NOT NULL COMMENT '酒店地址',
  `price` int(10) NOT NULL COMMENT '酒店价格',
  `score` int(2) NOT NULL COMMENT '酒店评分',
  `brand` varchar(32) NOT NULL COMMENT '酒店品牌',
  `city` varchar(32) NOT NULL COMMENT '所在城市',
  `star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级,1星到5星,1钻到5钻',
  `business` varchar(255) DEFAULT NULL COMMENT '商圈',
  `latitude` varchar(32) NOT NULL COMMENT '纬度',
  `longitude` varchar(32) NOT NULL COMMENT '经度',
  `pic` varchar(255) DEFAULT NULL COMMENT '酒店图片',
  `isAD` tinyint(4) DEFAULT NULL COMMENT '是否投放广告',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT

然后导入课前资料提供的项目:

项目结构如图:

image.png

15、HighLevelAPI-SpringBoot整合HighLevelAPI

1)导入相关依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>hotel-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hotel-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!-- 导入elasticsearch的RestAPI依赖 -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2)配置application.yml

spring:
  elasticsearch:
    rest:
      uris:
        - http://192.168.66.133:9200

3)编写测试类

package cn.itcast.hotel;

import org.elasticsearch.client.RestHighLevelClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 演示HighLevelRestClient的操作
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HotelDemoApplication.class )
public class HotelIndexTest {

    /**
     * 注入RestHighLevelClient对象
     */
    @Autowired
    private RestHighLevelClient highLevelClient;

    /**
     * 测试连接
     */
    @Test
    public void testConn(){
        System.out.println(highLevelClient);
    }

}

16、HighLevelAPI-创建索引

package cn.itcast.hotel;


import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;

/**
 * 演示HighLevelRestClient的操作
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HotelDemoApplication.class )
public class HotelIndexTest {

    /**
     * 注入RestHighLevelClient对象
     */
    @Autowired
    private RestHighLevelClient highLevelClient;

    /**
     * 测试连接
     */
    @Test
    public void testConn(){
        System.out.println(highLevelClient);
    }

    /**
     * 创建索引库
     */
    @Test
    public void testCreataIndex() throws Exception {
        //1.创建索引操作对象
        IndicesClient indicesClient = highLevelClient.indices();

        //2.创建请求(创建索引): 用于DSL语句
        CreateIndexRequest request = new CreateIndexRequest("hotel");  //hotel: 就是索引库名

        //3.执行请求:发送DSL
        CreateIndexResponse response = indicesClient.create(request, RequestOptions.DEFAULT);

        System.out.println(response.isAcknowledged());
    }

}

运行结果:

image.png

17、HighLevelAPI-创建索引并添加映射

# index属性:该字段是否索引,默认true
# all 为了把用户搜索条件聚合在一起设计的,方便搜索
# copy_to: 把字段值复制到另一个字段上面去


GET hotel/_mapping


PUT hotel
{
  "mappings": {
    "properties": {
      "id":{
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_smart",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword"
      },
      "price":{
         "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword"
      },
      "city":{
        "type": "keyword"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "text",
        "analyzer": "ik_smart",
        "copy_to": "all"
      },
      "location":{
         "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
         "type": "text",
         "analyzer": "ik_smart"
      }
    }
  }
}
/**
     * 创建索引库并设置映射
     */
    @Test
    public void testCreataIndexAndMapping() throws Exception {
        //1.创建索引操作对象
        IndicesClient indicesClient = highLevelClient.indices();

        //2.创建请求(创建索引): 用于DSL语句
        CreateIndexRequest request = new CreateIndexRequest("hotel");  //hotel: 就是索引库名

        //设置映射信息
        String source = "{\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\":{\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"name\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"ik_smart\",\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" +
                "      },\n" +
                "      \"starName\":{\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"business\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"copy_to\": \"all\"\n" +
                "      },\n" +
                "      \"location\":{\n" +
                "        \"type\": \"geo_point\"\n" +
                "      },\n" +
                "      \"pic\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"index\": false\n" +
                "      },\n" +
                "      \"isAD\":{\n" +
                "        \"type\": \"boolean\"\n" +
                "      },\n" +
                "      \"all\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"ik_smart\"\n" +
                "      }\n" +
                "      \n" +
                "    }\n" +
                "  }\n" +
                "}";

        request.source(source, XContentType.JSON);

        //3.执行请求:发送DSL
        CreateIndexResponse response = indicesClient.create(request, RequestOptions.DEFAULT);

        System.out.println(response.isAcknowledged());
    }

image.png

运行结果:

image.png

18、HighLevelAPI-删除索引

/**
     * 删除索引库
     */
    @Test
    public void testDeleteIndex() throws Exception {
        //1.创建索引操作对象
        IndicesClient indicesClient = highLevelClient.indices();

        //2.创建请求(删除索引): 用于DSL语句
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");

        //3.执行请求:发送DSL
        AcknowledgedResponse response = indicesClient.delete(request, RequestOptions.DEFAULT);

        System.out.println(response.isAcknowledged());
    }

19、HighLevelAPI-添加文档

/**
     * 新增文档
     */
    @Test
    public void testAddDoc() throws Exception{
        //1.查询一个酒店信息
        Hotel hotel = hotelService.getById(36934L);

        //存入ES
        //2.把Hotel转换为HotelDoc
        //在HoteDoc中设置字段属性与ES一致的格式;
        HotelDoc hotelDoc = new HotelDoc(hotel);

        //3.转换成json字符串
        String hotelJson = mapper.writeValueAsString(hotelDoc);

        //4.创建操作请求
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString()).source(hotelJson,XContentType.JSON);

        //5.执行请求
        IndexResponse response = highLevelClient.index(request, RequestOptions.DEFAULT);

        System.out.println(response.getId());
    }

20、HighLevelAPI-修改、查询、删除文档

1)修改文档

代码和添加文档一样,但修改必须已经存在ID

如果已经存在该id,那么ES就会自动进行覆盖;

/**
     * 修改文档
     */
    @Test
    public void testUpdateDoc() throws Exception{
        //1.查询一个酒店信息
        Hotel hotel = hotelService.getById(36934L);

        //2.把Hotel转换为HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        //修改价格
        hotelDoc.setPrice(386);

        //3.转换成json字符串
        String hotelJson = mapper.writeValueAsString(hotelDoc);

        //4.创建操作请求
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString()).source(hotelJson,XContentType.JSON);

        //5.执行请求
        IndexResponse response = highLevelClient.index(request, RequestOptions.DEFAULT);

        System.out.println(response.getId());
    }

2)* 查询文档

/**
     * 查询一个文档
     */
    @Test
    public void testFindDoc() throws Exception{
        Long id = 36934L;
          //1.创建请求
        GetRequest request = new GetRequest("hotel").id(id.toString());

        //2.执行请求
        GetResponse response = highLevelClient.get(request, RequestOptions.DEFAULT);

        //3.取出结果
        String hotelJson = response.getSourceAsString();

        HotelDoc hotelDoc = mapper.readValue(hotelJson, HotelDoc.class);

        System.out.println(hotelDoc);
    }

3)删除文档

      /**
     * 删除文档
     */
    @Test
    public void testDeleteDoc() throws IOException {
        Long id = 36934L;
        //1.创建请求.
        DeleteRequest request = new DeleteRequest("hotel").id(id.toString());

        //2.执行请求
        DeleteResponse response = highLevelClient.delete(request, RequestOptions.DEFAULT);

        System.out.println(response.getId());
    }

21、▲ HighLevelAPI-批量添加

目标:能够使用脚本和代码进行批量操作,一般在项目第一次部署的时候要进行;

Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

  • 在数据库的添加中,正常来说批量添加要比多次添加的执行速度要快;(前提是批量添加无出错)
  • 但是这个量值需要进行调优测试得出,要综合数据库的参数配置以及硬件性能等因素;

应用场景:ES索引库数据初始化的时候,可以将数据库的数据查询出来通过批量操作导入到索引库中

  • BulkRequest批量操作缓存对象;用于缓存执行的SQL语句; ```java package cn.itcast.hotel;

import cn.itcast.hotel.pojo.Hotel; import cn.itcast.hotel.pojo.HotelDoc; import cn.itcast.hotel.service.IHotelService; import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException; import java.util.List;

/**

  • @author Jztice5
  • @date 2022年05月12日 16:48 */

@SpringBootTest public class SQLTest {

//注入Service
@Autowired
private IHotelService ihotelService;
//注入HighLevelClient对象
@Autowired
private RestHighLevelClient restHighLevelClient;
//注入ObjectMapper对象用于JSON字符串的转换:

// @Autowired // private ObjectMapper objectMapper;

@Test
public void testSqlToES() throws IOException {
    //创建批量操作缓存对象
    BulkRequest bulkRequest = new BulkRequest();
    //从SQL中查询一批数据
    List <Hotel> list = ihotelService.list();


    //遍历list:
    //先判断是否为空;
    if (list.size() > 0 && list != null){
        for (Hotel hotel : list) {
            //将Hotel转为HotelDoc:
            HotelDoc hotelDoc = new HotelDoc(hotel);

            //转换为字符串:
            String hotelDocJSON = JSON.toJSONString(hotelDoc);
            //                String hotelJson = mapper.writeValueAsString(hotelDoc);

            //设置DSL语句:
            IndexRequest requestHotel = new IndexRequest("hotel").id(hotelDoc.getId().toString()).source(hotelDocJSON , XContentType.JSON);

            //把DSL放入缓存:
            bulkRequest.add(requestHotel);

        }
    }

    //执行 带有DSL语句 的请求,往ES中批量添加数据:
    BulkResponse response = restHighLevelClient.bulk(bulkRequest , RequestOptions.DEFAULT);

    System.out.println("response = " + response.status());

}

}

```

22、课程总结