一、认识ElasticSearch

(一)基于数据库查询的问题

ElasticSearch - 图1

需求: 查询title中包含‘手机’ 的信息?

实现:SELECT * FROM tb_item WHERE title LIKE ‘%手机%’;

问题:

(1)性能低:如果是模糊查询,坐标有通配符,不会走索引,会进行全表扫描,性能低。

(2)功能弱:如果以”苹果手机“作为条件,查询不出来数据。

解决:使用ES就可以倒排索引就可以解决上述存在的问题

(二)倒排索引

1 倒排索引概念

倒排索引:将文档进行分词,形成词条和id的对应关系即为反向索引。

2 倒排索引案例

以唐诗为例,所处包含“前”的诗句?

正向索引:由《静夜思》—>床前明月光—->“前”字

key value
<<静夜思>> 窗前明月光,疑是地上霜…
<<水调歌头>> 明月几时有,把酒问青天…
<<春晓>> 春眠不觉晓,处处闻啼鸟…

反向索引:

“床前明月光”—> 分词

将一段文本按照一定的规则,拆分为不同的词条(term)

ElasticSearch - 图2

案例:明月几时有,分词。分词效果如下

ElasticSearch - 图3

(三)ElasticSearch存储和查询的原理

index(索引):相当于mysql的库

映射:相当于mysql 的表结构

document(文档):相当于mysql的表中的数据

Es使用倒排索引,对title 进行分词

ElasticSearch - 图4

  1. 使用“手机”作为关键字查询
    生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度
  2. 使用“华为手机”作为关键字查询
    华为:1,3
    手机:1,2,3
    ElasticSearch - 图5

(四)ElasticSearch概念详解

ElasticSearch是一个基于Lucene的搜索服务器

ElasticSearch - 图6

是一个分布式、高扩展、高实时的搜索与数据分析引擎

基于RESTful web接口

Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎

官网:https://www.elastic.co/

应用场景

搜索:海量数据的查询

日志数据分析

实时数据分析

二、安装ElasticSearch

(一)ElasticSearch安装

1 上传ElasticSearch安装包

ElasticSearch - 图7

2 执行解压操作

  1. #创建一个安装目录
  2. mkdir /usr/local/es
  3. #将elasticsearch-7.4.0-linux-x86_64.tar.gz解压到/usr/local/es 目录下. -C 大写
  4. tar -zxvf elasticsearch-7.4.0-linux-x86_64.tar.gz -C /usr/local/es

3 创建普通用户

因为安全问题,Elasticsearch 不允许root用户直接运行,所以要创建新用户,在root用户中创建新用户,执行如下命令:

  1. useradd offcn #新增offcn账户
  2. passwd offcn #为offcn账户设置密码

4 为新用户授权,如下图

  1. chown -R offcn:offcn /usr/local/es/elasticsearch-7.4.0 #文件夹所有者

ElasticSearch - 图8

将 /usr/local/es/elasticsearch-7.4.0文件夹授权给offcn用户,由上图可见,我们的文件夹权限赋给了offcn

5 修改elasticsearch.yml文件

  1. vim /usr/local/es/elasticsearch-7.4.0/config/elasticsearch.yml
  1. # ======================== Elasticsearch Configuration =========================
  2. cluster.name: my-application
  3. node.name: node-1
  4. network.host: 0.0.0.0
  5. http.port: 9200
  6. cluster.initial_master_nodes: ["node-1"]
  7. #Centos6需要配置下面内容,因为不支持SecComp,而ES默认bootstrap.system_call_filter为true进行检测,可能会导致检测失败
  8. bootstrap.system_call_filter: false

cluster.name:配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称

node.name:节点名,elasticsearch会默认随机指定一个名字,建议指定一个有意义的名称,方便管理

network.host:设置为0.0.0.0允许外网访问

http.port:Elasticsearch的http访问端口

cluster.initial_master_nodes:初始化新的集群时需要此配置来选举master

6 修改虚拟机的配置文件

新创建的offcn用户最大可创建文件数太小,最大虚拟内存太小,切换到root用户,编辑下列配置文件, 添加类似如下内容

  1. # 切换到root用户
  2. su root
  3. #1. ===最大可创建文件数太小=======
  4. vim /etc/security/limits.conf
  5. # 在文件末尾中增加下面内容
  6. offcn soft nofile 65536
  7. offcn hard nofile 65536
  8. #===========
  9. vim /etc/security/limits.d/90-nproc.conf
  10. # 在文件末尾中增加下面内容
  11. # *和root最后对应的值修改大一点,改为4096,否则可能提示ERROR: [1] bootstrap checks failed,elasticsearch用户的最大线程数太低
  12. * soft nproc 4096
  13. root soft nproc 4096
  14. offcn soft nofile 65536
  15. offcn hard nofile 65536
  16. * hard nproc 4096
  17. # 注:* 代表Linux所有用户名称
  18. #2. ===最大虚拟内存太小=======
  19. vim /etc/sysctl.conf
  20. # 在文件中增加下面内容
  21. vm.max_map_count=262144
  22. # 重新加载,输入下面命令:
  23. /sbin/sysctl -p 立即生效

7 启动ElasticSearch

  1. su offcn #切换到offcn 用户启动
  2. cd /usr/local/es/elasticsearch-7.4.0/bin
  3. ./elasticsearch #启动

ElasticSearch - 图9

通过上图我们可以看到elasticsearch已经成功启动

8 查看ElasticSearch是否启动

  1. ps -ef|grep elastic

(二)访问ElasticSearch

1 关闭防火墙

  1. #暂时关闭防火墙
  2. systemctl stop firewalld.service
  3. # 或者
  4. #永久设置防火墙状态
  5. systemctl enable firewalld.service #打开防火墙永久性生效,重启后不会复原
  6. systemctl disable firewalld.service #关闭防火墙,永久性生效,重启后不会复原

2 浏览器访问

ElasticSearch - 图10

说明: 此时elasticsearch已成功启动

  1. 重点几个关注下即可:
  2. number" : "7.4.0" 表示elasticsearch版本
  3. lucene_version" : "8.2.0" 表示lucene版本
  4. name 默认启动的时候指定了 ES 实例名称
  5. cluster_name 默认名为 elasticsearch

三、ElasticSearch辅助工具安装

(一) Postman 介绍和安装

Postman是一个http模拟请求的工具。

官网介绍:“Modern software is built on APIs,Postman helps you develop APIs faster”

看得出来,它是一个专门测试 API 的工具,Postman 提供功能强大的 Web API 和 HTTP 请求的调试,它能够发送任何类型的HTTP 请求 (GET, POST, PUT, DELETE…),并且能附带任何数量的参数和 Headers。不仅如此,它还提供测试数据和环境配置数据的导入导出。

进入官网www.getpostman.com,下载

(二) Kibana介绍&安装

1 什么是Kibana

Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。

Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示Elasticsearch查询动态。

下载地址:https://www.elastic.co/cn/downloads/past-releases#kibana

2 Kibana安装

2.1 上传kibana

上传到虚拟机。

2.2 解压kibana到安装目录

  1. #创建安装目录
  2. mkdir /usr/local/kibana
  3. #将kibana解压到安装目录
  4. tar -zxvf kibana-7.4.0-linux-x86_64.tar.gz -C /usr/local/kibana

2.3 修改kibana配置

  1. vim /usr/local/kibana/kibana-7.4.0-linux-x86_64/config/kibana.yml
  1. server.port: 5601
  2. server.host: "0.0.0.0"
  3. server.name: "kibana-offcn"
  4. elasticsearch.hosts: ["http://127.0.0.1:9200"]
  5. elasticsearch.requestTimeout: 99999

server.port:http访问端口

server.host:ip地址,0.0.0.0表示可远程访问

server.name:kibana服务名

elasticsearch.hosts:elasticsearch地址

elasticsearch.requestTimeout:请求elasticsearch超时时间,默认为30000,此处可根据情况设置

2.4 启动kibana

由于kibana不建议使用root用户启动,如果用root启动,需要加—allow-root参数

  1. # 切换到kibana的bin目录
  2. cd /usr/local/kibana/kibana-7.4.0-linux-x86_64/bin
  3. # 启动
  4. ./kibana --allow-root

ElasticSearch - 图11

启动成功。

2.5 访问kibane

  1. 访问地址: http://192.168.126.20:5601/

ElasticSearch - 图12

四、ElasticSearch核心概念

索引(index)

ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。

映射(mapping)

mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。

文档(document)

Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。

倒排索引

一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表。

类型(type)

一种type就像一类表。如用户表、角色表等。在Elasticsearch7.X默认type为_doc

  1. ES 5.x中一个index可以有多种type
  2. ES 6.x中一个index只能有一种type
  3. ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc

五、脚本操作ElasticSearch

(一)RESTful风格介绍

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,可以降低开发的复杂性,提高系统的可伸缩性。

特点:

  1. 1.基于http协议
  2. 2.使用XML格式定义或JSON格式定义
  3. 3.每一个URI代表1种资源。
  4. 4.客户端使用GETPOSTPUTDELETE 4个表示操作方式的动词对服务端资源进行操作:
  5. GET:用来获取资源
  6. POST:用来新建资源(也可以用于更新资源)
  7. PUT:用来更新资源
  8. DELETE:用来删除资源
方式 说明
/orgz/members GET 获取成员列表
/orgz/members/120 GET 获取成员列表
/orgz/members POST 创建成员
/orgz/members/120 PUT 修改成员
/orgz/members PUT 批量修改
/orgz/members/120 PATCH 修改成员的部分属性
/orgz/members/120 DELETE 删除成员

(二)操作索引

1 创建索引(PUT)

  1. http://ip:端口/索引名称
  2. 案例:
  3. PUT http://192.168.126.20:9200/index_01

2 查询索引(GET)

  1. GET http://ip:端口/索引名称 # 查询单个索引信息
  2. GET http://ip:端口/索引名称1,索引名称2... # 查询多个索引信息
  3. GET http://ip:端口/_all # 查询所有索引信息

3 删除索引(DELETE)

  1. DELETE http://ip:端口/索引名称

4 关闭、打开索引(了解)

  1. POST http://ip:端口/索引名称/_close
  2. POST http://ip:端口/索引名称/_open

(三)ElasticSearch 数据类型

1 简单数据类型

数据类型 备注
字符串 text :会分词,不支持聚合 相当于mysql 中的sum(求和)
keyword:不会分词,将全部内容作为一个词条,支持聚合
数值 byte, short, integer, long, float, double
布尔 boolean
范围类型 integer_range, float_range, long_range, double_range, date_range
日期 date

2 复杂数据类型

数据类型 备注
数组: [ ] nested (for arrays of JSON objects 数组类型的JSON对象)
对象:{ } object(for single JSON objects 单个JSON对象)

(四)操作映射

1 查询映射

  1. GET /索引的名称/_mapping

2 创建映射

2.1 创建索引后添加映射

  1. #创建一个person 索引
  2. PUT person
  3. #查询person 索引的mapping 是空
  4. GET /person/_mapping
  5. #给person 添加索引
  6. PUT /person/_mapping
  7. {
  8. "properties":{
  9. "name":{
  10. "type":"text"
  11. },
  12. "age":{
  13. "type":"integer"
  14. }
  15. }
  16. }

2.2 创建索引并添加映射

  1. #创建索引并且添加映射
  2. PUT person2
  3. {
  4. "mappings": {
  5. "properties": {
  6. "name":{
  7. "type":"text"
  8. },
  9. "age":{
  10. "type": "integer"
  11. }
  12. }
  13. }
  14. }
  15. #查询映射
  16. GET person2/_mapping

添加字段

  1. #给创建好的索引添加字段
  2. PUT /person2/_mapping
  3. {
  4. "properties":{
  5. "address":{
  6. "type":"text"
  7. }
  8. }
  9. }

总结:在实际当中 ,我们很少操作映射,一般都是使用API方式操作文档,所以以上的操作只需要能看懂就可以。

(五) 操作文档

1 查询文档

  1. #根据id查询具体的文档
  2. 语法: GET /索引的名称/_doc/索引的id
  3. 案例: GET /person1/_doc/1
  4. #查询所有的文档
  5. 语法:GET /索引名称/_search
  6. 案例:GET /person1/_search

2 添加文档,指定id

  1. #语法:POST /索引的名称/文档的类型/文档的id
  2. POST /person2/_doc/1
  3. {
  4. "name":"张三",
  5. "age":18,
  6. "address":"北京"
  7. }
  8. #查询文档
  9. GET /person2/_doc/1

添加文档,不指定id

  1. #添加文档,不指定id
  2. POST /person1/_doc/
  3. {
  4. "name":"张三",
  5. "age":18,
  6. "address":"北京"
  7. }
  8. #查询所有文档
  9. GET /person1/_search

3 删除文档

  1. #删除指定id文档
  2. DELETE /person1/_doc/1

六、IK分词器

(一)IK分词器安装

1 环境准备

Elasticsearch 要使用 ik,就要先构建 ik 的 jar包,这里要用到 maven 包管理工具,而 maven 需要java 环境,而 Elasticsearch 内置了jdk, 所以可以将JAVA_HOME设置为Elasticsearch 内置的jdk

1.1 设置JAVA_HOME

  1. vim /etc/profile
  2. # 在profile文件末尾添加
  3. #java environment
  4. export JAVA_HOME=/usr/local/es/elasticsearch-7.4.0/jdk
  5. export PATH=$PATH:${JAVA_HOME}/bin
  6. # 保存退出后,重新加载profile
  7. source /etc/profile

1.2 下载maven安装包

  1. #安装包较小,直接使用wget命令下载,下载的默认位置,当前所在的位置
  2. wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz

1.3 解压maven安装包

  1. #创建maven的安装包
  2. mkdir /usr/local/maven/
  3. #解压到指定的目录
  4. tar -zxvf apache-maven-3.1.1-bin.tar.gz -C /usr/local/maven/

1.4 设置软连接

  1. ln -s apache-maven-3.1.1 maven

1.5 设置path

  1. #打开文件
  2. vim /etc/profile.d/maven.sh
  3. #将下面的内容复制到文件,保存
  4. export MAVEN_HOME=/usr/local/maven/maven
  5. export PATH=${MAVEN_HOME}/bin:${PATH}
  6. #设置好Maven的路径之后,需要运行下面的命令使其生效
  7. source /etc/profile.d/maven.sh

1.6 验证maven是否安装成功

  1. mvn -v

1.7 设置maven的阿里云镜像

  1. <mirror>
  2. <id>alimaven</id>
  3. <name>aliyun maven</name>
  4. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  5. <mirrorOf>central</mirrorOf>
  6. </mirror>

2 安装IK分词器

2.1 下载IK分词器

  1. #直接使用wget命令下载,下载的默认位置,当前所在的位置
  2. wget https://github.com/medcl/elasticsearch-analysis-ik/archive/v7.4.0.zip

2.2 解压IK到指定目录

由于这里是zip包不是gz包,所以我们需要使用unzip命令进行解压,如果本机环境没有安装unzip,请执行:

  1. yum install zip
  2. yum install unzip

解压IK

  1. unzip v7.4.0.zip

2.3 编译jar包

  1. # 切换到 elasticsearch-analysis-ik-7.4.0目录
  2. cd elasticsearch-analysis-ik-7.4.0/
  3. #打包,注意第一次使用maven会比较慢,提前配置好阿里云镜像
  4. mvn package

2.4 jar包移动

  1. #切换目录
  2. cd /usr/local/es/elasticsearch-7.4.0/plugins/
  3. #新建目录
  4. mkdir analysis-ik
  5. cd analysis-ik
  6. #执行拷贝
  7. cp -R /opt/elasticsearch-analysis-ik-7.4.0/target/releases/elasticsearch-analysis-ik-7.4.0.zip /usr/local/es/elasticsearch-7.4.0/plugins/analysis-ik
  8. #执行解压
  9. unzip /usr/local/es/elasticsearch-7.4.0/plugins/analysis-ik/elasticsearch-analysis-ik-7.4.0.zip

2.5 拷贝词典

将elasticsearch-analysis-ik-7.4.0目录下的config目录中的所有文件 拷贝到elasticsearch的config目录

  1. cp -R /opt/elasticsearch-analysis-ik-7.4.0/config/* /usr/local/es/elasticsearch-7.4.0/config

2.6 重新启动

配置成功之后,重新启动ES,重新启动Kibana,使用IK分词器。

(二)使用IK分词器

1 IK分词器使用

IK分词器有两种分词模式:ik_max_word和ik_smart模式。

1.1ik_max_word

会将文本做最细粒度的拆分,比如会将“乒乓球明年总冠军”拆分为“乒乓球、乒乓、球、明年、总冠军、冠军。

  1. #方式一 ik_max_word
  2. GET /_analyze
  3. {
  4. "analyzer": "ik_max_word",
  5. "text": "乒乓球明年总冠军"
  6. }

ik_max_word分词器执行如下:

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "乒乓球",
  5. "start_offset" : 0,
  6. "end_offset" : 3,
  7. "type" : "CN_WORD",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "乒乓",
  12. "start_offset" : 0,
  13. "end_offset" : 2,
  14. "type" : "CN_WORD",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "球",
  19. "start_offset" : 2,
  20. "end_offset" : 3,
  21. "type" : "CN_CHAR",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "明年",
  26. "start_offset" : 3,
  27. "end_offset" : 5,
  28. "type" : "CN_WORD",
  29. "position" : 3
  30. },
  31. {
  32. "token" : "总冠军",
  33. "start_offset" : 5,
  34. "end_offset" : 8,
  35. "type" : "CN_WORD",
  36. "position" : 4
  37. },
  38. {
  39. "token" : "冠军",
  40. "start_offset" : 6,
  41. "end_offset" : 8,
  42. "type" : "CN_WORD",
  43. "position" : 5
  44. }
  45. ]
  46. }

1.2 ik_smart

会做最粗粒度的拆分,比如会将“乒乓球明年总冠军”拆分为乒乓球、明年、总冠军。

  1. #方式二ik_smart
  2. GET /_analyze
  3. {
  4. "analyzer": "ik_smart",
  5. "text": "乒乓球明年总冠军"
  6. }

ik_smart分词器执行如下:

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "乒乓球",
  5. "start_offset" : 0,
  6. "end_offset" : 3,
  7. "type" : "CN_WORD",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "明年",
  12. "start_offset" : 3,
  13. "end_offset" : 5,
  14. "type" : "CN_WORD",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "总冠军",
  19. "start_offset" : 5,
  20. "end_offset" : 8,
  21. "type" : "CN_WORD",
  22. "position" : 2
  23. }
  24. ]
  25. }

由此可见 使用ik_smart可以将文本”text”: “乒乓球明年总冠军”分成了【乒乓球】【明年】【总冠军】

这样看的话,这样的分词效果达到了我们的要求。

1.3 使用IK分词器查询文档

  • 词条查询:term

    1. 词条查询不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配搜索
  • 全文查询:match

    1. 全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集

1.创建索引,添加映射,并指定分词器为ik分词器
  1. PUT person2
  2. {
  3. "mappings": {
  4. "properties": {
  5. "name": {
  6. "type": "keyword"
  7. },
  8. "address": {
  9. "type": "text",
  10. "analyzer": "ik_max_word"
  11. },
  12. "age":{
  13. "type":"integer"
  14. }
  15. }
  16. }
  17. }

2.添加文档
  1. POST /person2/_doc/1
  2. {
  3. "name":"张三",
  4. "age":18,
  5. "address":"北京海淀区"
  6. }
  7. POST /person2/_doc/2
  8. {
  9. "name":"李四",
  10. "age":18,
  11. "address":"北京朝阳区"
  12. }
  13. POST /person2/_doc/3
  14. {
  15. "name":"王五",
  16. "age":18,
  17. "address":"北京昌平区"
  18. }
  19. POST /person2/_doc/4
  20. {
  21. "name":"赵六",
  22. "age":18,
  23. "address":"昌平区慧聪园"
  24. }

3.查询映射
  1. GET person2

ElasticSearch - 图13

4.查看分词效果
  1. GET _analyze
  2. {
  3. "analyzer": "ik_max_word",
  4. "text": "北京海淀"
  5. }

5.词条查询:term

查询person2中匹配到”北京”两字的词条

  1. GET /person2/_search
  2. {
  3. "query": {
  4. "term": {
  5. "address": {
  6. "value": "北京"
  7. }
  8. }
  9. }
  10. }

6.全文查询:match

全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集

  1. GET /person2/_search
  2. {
  3. "query": {
  4. "match": {
  5. "address":"北京昌平"
  6. }
  7. }
  8. }

七、 ElasticSearch JavaAPI

(一)SpringBoot整合ElasticSearch环境搭建

1 搭建SpringBoot工程

2 引入ElasticSearch相关坐标

  1. <!--引入es的坐标-->
  2. <dependency>
  3. <groupId>org.elasticsearch.client</groupId>
  4. <artifactId>elasticsearch-rest-high-level-client</artifactId>
  5. <version>7.4.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.elasticsearch.client</groupId>
  9. <artifactId>elasticsearch-rest-client</artifactId>
  10. <version>7.4.0</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.elasticsearch</groupId>
  14. <artifactId>elasticsearch</artifactId>
  15. <version>7.4.0</version>
  16. </dependency>
  17. <!-- 数据类型转换 -->
  18. <dependency>
  19. <groupId>com.alibaba</groupId>
  20. <artifactId>fastjson</artifactId>
  21. <version>1.2.60</version>
  22. </dependency>

3 编写核心配置类

编写核心配置文件:

  1. elasticsearch:
  2. host: 192.168.126.20
  3. port: 9200

编写核心配置类

  1. @Configuration
  2. @ConfigurationProperties(prefix="elasticsearch")
  3. public class ElasticSearchConfig {
  4. private String host;
  5. private int port;
  6. @Bean
  7. public RestHighLevelClient client(){
  8. return new RestHighLevelClient(RestClient.builder(
  9. new HttpHost(host,port,"http")
  10. ));
  11. }
  12. public String getHost() {
  13. return host;
  14. }
  15. public void setHost(String host) {
  16. this.host = host;
  17. }
  18. public int getPort() {
  19. return port;
  20. }
  21. public void setPort(int port) {
  22. this.port = port;
  23. }
  24. }

4 测试客户端对象

注意:使用@Autowired注入RestHighLevelClient 如果报红线,则是因为配置类所在的包和测试类所在的包,包名不一致造成的

  1. @SpringBootTest
  2. class SpringElasticsearchApplicationTests {
  3. @Autowired
  4. private RestHighLevelClient restHighLevelClient;
  5. @Test
  6. void contextLoads() {
  7. System.out.println(restHighLevelClient);
  8. }
  9. }

(二)操作ElasticSearch

1 添加索引

  1. /**
  2. * 添加索引:
  3. * @throws Exception
  4. */
  5. @Test
  6. public void addIndex() throws Exception{
  7. //1:使用restHighLevelClient获取操作索引的对象
  8. IndicesClient indices = restHighLevelClient.indices();
  9. //2: 具体操作索引,并且获得返回值
  10. //2.1 创建索引对象,设置索引的名称
  11. CreateIndexRequest createIndexRequest = new CreateIndexRequest("offcn");
  12. //2.2 创建索引,获得返回值
  13. CreateIndexResponse createIndexResponse = indices.create(createIndexRequest, RequestOptions.DEFAULT);
  14. //3:根据返回值判断返回结果
  15. System.out.println(createIndexResponse.isAcknowledged());
  16. }

2 添加索引,并添加映射

  1. /**
  2. * 创建索引并且添加映射
  3. * @throws IOException
  4. */
  5. @Test
  6. public void addIndexAndMapping() throws IOException {
  7. //1.使用client获取操作索引的对象
  8. IndicesClient indicesClient = restHighLevelClient.indices();
  9. //2.具体操作,获取返回值
  10. CreateIndexRequest createRequest = new CreateIndexRequest("offcn2");
  11. //2.1 设置mappings
  12. String mapping = "{\n" +
  13. " \"properties\" : {\n" +
  14. " \"address\" : {\n" +
  15. " \"type\" : \"text\",\n" +
  16. " \"analyzer\" : \"ik_max_word\"\n" +
  17. " },\n" +
  18. " \"age\" : {\n" +
  19. " \"type\" : \"long\"\n" +
  20. " },\n" +
  21. " \"name\" : {\n" +
  22. " \"type\" : \"keyword\"\n" +
  23. " }\n" +
  24. " }\n" +
  25. " }";
  26. createRequest.mapping(mapping, XContentType.JSON);
  27. //2.2执行创建,返回对象
  28. CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
  29. //3.根据返回值判断结果
  30. System.out.println(response.isAcknowledged());
  31. }

3 查询、删除、判断索引

3.1 查询索引

  1. /**
  2. * 查询索引
  3. */
  4. @Test
  5. public void queryIndex() throws IOException {
  6. //1:使用client获取操作索引的对象
  7. IndicesClient indices = restHighLevelClient.indices();
  8. //2:获得对象,执行具体的操作
  9. //2.1 创建获取索引的请求对象,设置索引名称
  10. GetIndexRequest getReqeust = new GetIndexRequest("offcn");
  11. //2.2 执行查询,获得返回值
  12. GetIndexResponse response = indices.get(getReqeust, RequestOptions.DEFAULT);
  13. //3:获取结果,遍历
  14. Map<String, MappingMetaData> mappings = response.getMappings();
  15. for (String key : mappings.keySet()) {
  16. System.out.println(key + ":" + mappings.get(key).getSourceAsMap());
  17. }
  18. }

3.2 删除索引

  1. /**
  2. * 删除索引
  3. */
  4. @Test
  5. public void deleteIndex() throws IOException {
  6. IndicesClient indices = restHighLevelClient.indices();
  7. DeleteIndexRequest deleteRequest = new DeleteIndexRequest("offcn");
  8. AcknowledgedResponse response = indices.delete(deleteRequest, RequestOptions.DEFAULT);
  9. System.out.println(response.isAcknowledged());
  10. }

3.3 索引是否存在

  1. /**
  2. * 判断索引是否存在
  3. */
  4. @Test
  5. public void existIndex() throws IOException {
  6. IndicesClient indices = restHighLevelClient.indices();
  7. GetIndexRequest getRequest = new GetIndexRequest("person");
  8. boolean exists = indices.exists(getRequest, RequestOptions.DEFAULT);
  9. System.out.println(exists);
  10. }

4 操作文档

4.1 添加文档

添加文档,使用map作为数据

  1. /**
  2. * 添加文档,使用map作为数据
  3. */
  4. @Test
  5. public void addDoc() throws IOException {
  6. //数据对象,map
  7. Map data = new HashMap();
  8. data.put("address", "北京昌平");
  9. data.put("name", "马同志");
  10. data.put("age", 20);
  11. //1:获取操作文档的对象
  12. IndexRequest request = new IndexRequest("offcn").id("1").source(data);
  13. //2:添加数据,获取结果
  14. IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
  15. //3:打印响应结果
  16. System.out.println(response.getId());
  17. }

添加文档,使用对象作为数据

  1. /**
  2. * 添加文档,使用对象作为数据
  3. */
  4. @Test
  5. public void addDoc2() throws IOException {
  6. //数据对象,javaObject
  7. Person p = new Person();
  8. p.setName("小胖2222");
  9. p.setAge(30);
  10. p.setAddress("陕西西安");
  11. //将对象转为json
  12. String data = JSON.toJSONString(p);
  13. //1:获取操作文档的对象 注意添加类型JSON
  14. IndexRequest request = new IndexRequest("offcn").id(p.getId()).source(data, XContentType.JSON);
  15. //2:添加数据,获取结果
  16. IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
  17. //3:打印响应结果
  18. System.out.println(response.getId());
  19. }

4.2 修改文档:添加文档时,如果id存在则修改,id不存在则添加

  1. /**
  2. * 修改文档:添加文档时,如果id存在则修改,id不存在则添加
  3. */
  4. @Test
  5. public void updateDoc() throws IOException {
  6. //数据对象,map
  7. Map data = new HashMap();
  8. data.put("address", "北京昌平");
  9. data.put("name", "朱同志");
  10. data.put("age", 20);
  11. //1:获取操作文档的对象
  12. IndexRequest request = new IndexRequest("offcn").id("1").source(data);
  13. //2:添加数据,获取结果
  14. IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
  15. //3:打印响应结果
  16. System.out.println(response.getId());
  17. }

4.3 根据id查询文档

  1. /**
  2. * 根据id查询文档
  3. */
  4. @Test
  5. public void findDocById() throws IOException {
  6. GetRequest getReqeust = new GetRequest("offcn", "1");
  7. //getReqeust.id("1");
  8. GetResponse response = restHighLevelClient.get(getReqeust, RequestOptions.DEFAULT);
  9. //获取数据对应的json
  10. System.out.println(response.getSourceAsString());
  11. }

4.4 根据id删除文档

  1. /**
  2. * 根据id删除文档
  3. */
  4. @Test
  5. public void delDoc() throws IOException {
  6. DeleteRequest deleteRequest = new DeleteRequest("offcn", "1");
  7. DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
  8. System.out.println(response.getId());
  9. }

(三)ElasticSearch的批量操作

1 bulk 批量操作脚本实现

需求:同时完成删除,更新,添加操作

  1. POST _bulk
  2. {"delete":{"_index":"person1","_id":"4"}}
  3. {"create":{"_index":"person1","_id":"5"}}
  4. {"name":"八号","age":18,"address":"北京"}
  5. {"update":{"_index":"person1","_id":"2"}}
  6. {"doc":{"name":"2号"}}
  7. 查询运行结果~

2 bulk批量操作JavaAPI 实现

  1. /**
  2. * Bulk 批量操作
  3. */
  4. @Test
  5. public void test2() throws IOException {
  6. //创建bulkrequest对象,整合所有操作
  7. BulkRequest bulkRequest =new BulkRequest();
  8. /*
  9. # 1. 删除5号记录
  10. # 2. 添加6号记录
  11. # 3. 修改3号记录 名称为 “三号”
  12. */
  13. //添加对应操作
  14. //1. 删除5号记录
  15. DeleteRequest deleteRequest=new DeleteRequest("person1","5");
  16. bulkRequest.add(deleteRequest);
  17. //2. 添加6号记录
  18. Map<String, Object> map=new HashMap<>();
  19. map.put("name","六号");
  20. IndexRequest indexRequest=new IndexRequest("person1").id("6").source(map);
  21. bulkRequest.add(indexRequest);
  22. //3. 修改3号记录 名称为 “三号”
  23. Map<String, Object> mapUpdate=new HashMap<>();
  24. mapUpdate.put("name","三号");
  25. UpdateRequest updateRequest=new UpdateRequest("person1","3").doc(mapUpdate);
  26. bulkRequest.add(updateRequest);
  27. //执行批量操作
  28. BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
  29. System.out.println(response.status());
  30. }

3 导入数据分析&创建索引

需求: 将数据库中Good表的数据导入到ElasticSearch当中

实现步骤:

分析goods表结构

创建goods索引

查询Goods表数据

批量添加到ElasticSearch中

  1. PUT goods
  2. {
  3. "mappings": {
  4. "properties": {
  5. "title": {
  6. "type": "text",
  7. "analyzer": "ik_smart"
  8. },
  9. "price": {
  10. "type": "double"
  11. },
  12. "createTime": {
  13. "type": "date"
  14. },
  15. "categoryName": {
  16. "type": "keyword"
  17. },
  18. "brandName": {
  19. "type": "keyword"
  20. },
  21. "spec": {
  22. "type": "object"
  23. },
  24. "saleNum": {
  25. "type": "integer"
  26. },
  27. "stock": {
  28. "type": "integer"
  29. }
  30. }
  31. }
  32. }

title:商品标题

price:商品价格

createTime:创建时间

categoryName:分类名称。如:家电,手机

brandName:品牌名称。如:华为,小米

spec: 商品规格。如: spec:{“屏幕尺寸”,”5寸”,”内存大小”,”128G”}

saleNum:销量

stock:库存量

插入一条测试数据

  1. POST goods/_doc/1
  2. {
  3. "title":"小米手机",
  4. "price":1000,
  5. "createTime":"2021-12-01",
  6. "categoryName":"手机",
  7. "brandName":"小米",
  8. "saleNum":3000,
  9. "stock":10000,
  10. "spec":{
  11. "网络制式":"移动4G",
  12. "屏幕尺寸":"4.5"
  13. }
  14. }

4 导入数据代码实现

  1. /**
  2. * 从Mysql 批量导入 elasticSearch
  3. */
  4. @Test
  5. public void test3() throws IOException {
  6. //1.查询所有数据,mysql
  7. List<Goods> goodsList = goodsMapper.findAll();
  8. //2.bulk导入
  9. BulkRequest bulkRequest=new BulkRequest();
  10. //2.1 循环goodsList,创建IndexRequest添加数据
  11. for (Goods goods : goodsList) {
  12. //2.2 设置spec规格信息 Map的数据 specStr:{}
  13. String specStr = goods.getSpecStr();
  14. //将json格式字符串转为Map集合
  15. Map map = JSON.parseObject(specStr, Map.class);
  16. //设置spec map
  17. goods.setSpec(map);
  18. //将goods对象转换为json字符串
  19. String data = JSON.toJSONString(goods);
  20. IndexRequest indexRequest=new IndexRequest("goods").id(goods.getId()+"").source(data,XContentType.JSON);
  21. bulkRequest.add(indexRequest);
  22. }
  23. BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
  24. System.out.println(response.status());
  25. }

(四)ElasticSearch高级查询

1 matchAll脚本实现

  1. # 默认情况下,es一次展示10条数据,通过fromsize来控制分页
  2. # 查询结果详解
  3. GET goods/_search
  4. {
  5. "query": {
  6. "match_all": {}
  7. },
  8. "from": 0,
  9. "size": 100
  10. }
  11. GET goods

2 matchAll-JavaAPI

  1. /**
  2. * 查询所有
  3. * 1. matchAll
  4. * 2. 将查询结果封装为Goods对象,装载到List中
  5. * 3. 分页。默认显示10条
  6. */
  7. @Test
  8. public void matchAll() throws IOException {
  9. //2. 构建查询请求对象,指定查询的索引名称
  10. SearchRequest searchRequest=new SearchRequest("goods");
  11. //4. 创建查询条件构建器SearchSourceBuilder
  12. SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
  13. //6. 查询条件
  14. QueryBuilder queryBuilder= QueryBuilders.matchAllQuery();
  15. //5. 指定查询条件
  16. sourceBuilder.query(queryBuilder);
  17. //3. 添加查询条件构建器 SearchSourceBuilder
  18. searchRequest.source(sourceBuilder);
  19. // 8 . 添加分页信息 不设置 默认10条
  20. // sourceBuilder.from(0);
  21. // sourceBuilder.size(100);
  22. //1. 查询,获取查询结果
  23. SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
  24. //7. 获取命中对象 SearchHits
  25. SearchHits hits = searchResponse.getHits();
  26. //7.1 获取总记录数
  27. Long total= hits.getTotalHits().value;
  28. System.out.println("总数:"+total);
  29. //7.2 获取Hits数据 数组
  30. SearchHit[] hits1 = hits.getHits();
  31. //获取json字符串格式的数据
  32. List<Goods> goodsList = new ArrayList<>();
  33. for (SearchHit searchHit : hits1) {
  34. String sourceAsString = searchHit.getSourceAsString();
  35. //转为java对象
  36. Goods goods = JSON.parseObject(sourceAsString, Goods.class);
  37. goodsList.add(goods);
  38. }
  39. for (Goods goods : goodsList) {
  40. System.out.println(goods);
  41. }
  42. }

3 termQuery 词条查询

term查询和字段类型有关系,首先回顾一下ElasticSearch两个数据类型

ElasticSearch两个数据类型:

  1. text:会分词,不支持聚合
  2. keyword:不会分词,将全部内容作为一个词条,支持聚合

脚本实现:

  1. #term查询: 查询title当中包含 华为
  2. GET /goods/_search
  3. {
  4. "query": {
  5. "term": {
  6. "title": {
  7. "value": "华为"
  8. }
  9. }
  10. }
  11. }

javaAPI代码实现:

  1. /**
  2. * termQuery:词条查询
  3. */
  4. @Test
  5. public void testTermQuery() throws IOException {
  6. SearchRequest searchRequest = new SearchRequest("goods");
  7. SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
  8. QueryBuilder query = QueryBuilders.termQuery("title","华为");//term词条查询
  9. sourceBulider.query(query);
  10. searchRequest.source(sourceBulider);
  11. SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
  12. SearchHits searchHits = searchResponse.getHits();
  13. //获取记录数
  14. long value = searchHits.getTotalHits().value;
  15. System.out.println("总记录数:"+value);
  16. List<Goods> goodsList = new ArrayList<>();
  17. SearchHit[] hits = searchHits.getHits();
  18. for (SearchHit hit : hits) {
  19. String sourceAsString = hit.getSourceAsString();
  20. //转为java
  21. Goods goods = JSON.parseObject(sourceAsString, Goods.class);
  22. goodsList.add(goods);
  23. }
  24. for (Goods goods : goodsList) {
  25. System.out.println(goods);
  26. }
  27. }

4 matchQuery

match查询,会对查询条件分词,然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR)

  1. # match查询
  2. GET goods/_search
  3. {
  4. "query": {
  5. "match": {
  6. "title": "华为手机"
  7. }
  8. },
  9. "size": 500
  10. }

match 的默认搜索(or 并集)

例如:华为手机,会分词为 “华为”,“手机” 只要出现其中一个词条都会搜索到

match的 and(交集) 搜索

例如:例如:华为手机,会分词为 “华为”,“手机” 但要求“华为”,和“手机”同时出现在词条

总结:

term query会去倒排索引中寻找确切的term,它并不知道分词器的存在。这种查询适合keywordnumericdate

match query知道分词器的存在。并且理解是如何被分词的

5 模糊查询脚本实现

  1. 通配符使用,?代表任意单个字符,*代表0个或者是多个字符
  2. 案例:
  3. "*华*" 包含华字的
  4. "华*" 华字后边多个字符
  5. "华?" 华字后边单个字符
  6. "*华""?华" 会引发全表(全索引)扫描 注意效率问题
  1. # wildcard 查询。查询条件分词,模糊查询
  2. GET goods/_search
  3. {
  4. "query": {
  5. "wildcard": {
  6. "title": {
  7. "value": "华*"
  8. }
  9. }
  10. }
  11. }

6 模糊查询javaAPI

  1. //模糊查询
  2. WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");//华后多个字符

7 范围查询脚本实现

  1. # 范围查询
  2. GET goods/_search
  3. {
  4. "query": {
  5. "range": {
  6. "price": {
  7. "gte": 2000,
  8. "lte": 3000
  9. }
  10. }
  11. },
  12. "sort": [
  13. {
  14. "price": {
  15. "order": "desc"
  16. }
  17. }
  18. ]
  19. }

8 范围查询API实现

  1. //范围查询 以price 价格为条件
  2. RangeQueryBuilder query = QueryBuilders.rangeQuery("price");
  3. //指定下限
  4. query.gte(2000);
  5. //指定上限
  6. query.lte(3000);
  7. sourceBuilder.query(query);
  8. //排序 价格 降序排列
  9. sourceBuilder.sort("price",SortOrder.DESC);

9 queryString 查询

queryString 多条件查询

会对查询条件进行分词。

然后将分词后的查询条件和词条进行等值匹配

默认取并集(OR)

可以指定多个查询字段

query_string:识别query中的连接符(or 、and)

  1. # queryString
  2. GET goods/_search
  3. {
  4. "query": {
  5. "query_string": {
  6. "fields": ["title","categoryName","brandName"],
  7. "query": "华为 AND 手机"
  8. }
  9. }
  10. }

simple_query_string:不识别query中的连接符(or 、and),查询时会将 “华为”、”and”、“手机”分别进行查询

  1. GET goods/_search
  2. {
  3. "query": {
  4. "simple_query_string": {
  5. "fields": ["title","categoryName","brandName"],
  6. "query": "华为 AND 手机"
  7. }
  8. }
  9. }

10 queryString javaAPI代码实现:

  1. QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机").field("title").field("categoryName")
  2. .field("brandName").defaultOperator(Operator.AND);

11 聚合查询脚本实现

指标聚合:相当于MySQL的聚合函数。max、min、avg、sum等

桶聚合:相当于MySQL的 group by 操作。不要对text类型的数据进行分组,会失败

  1. # 聚合查询
  2. # 指标聚合 聚合函数
  3. GET goods/_search
  4. {
  5. "query": {
  6. "match": {
  7. "title": "手机"
  8. }
  9. },
  10. "aggs": {
  11. "max_price": {
  12. "max": {
  13. "field": "price"
  14. }
  15. }
  16. }
  17. }
  18. # 桶聚合 分组
  19. GET goods/_search
  20. {
  21. "query": {
  22. "match": {
  23. "title": "手机"
  24. }
  25. },
  26. "aggs": {
  27. "goods_brands": {
  28. "terms": {
  29. "field": "brandName",
  30. "size": 100
  31. }
  32. }
  33. }
  34. }

12 聚合查询JavaAPI

聚合查询:桶聚合,分组查询

  1. 查询title包含手机的数据
  2. 查询品牌列表 ```java /**

    • 聚合查询:桶聚合,分组查询
      1. 查询title包含手机的数据
      1. 查询品牌列表 */ @Test public void testAggQuery() throws IOException {

    SearchRequest searchRequest=new SearchRequest(“goods”);

    SearchSourceBuilder sourceBuilder=new SearchSourceBuilder(); //1. 查询title包含手机的数据

    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(“title”, “手机”);

    sourceBuilder.query(matchQueryBuilder); //2. 查询品牌列表 只展示前100条 AggregationBuilder aggregation=AggregationBuilders.terms(“goods”).field(“brandName”).size(100); sourceBuilder.aggregation(aggregation);

  1. searchRequest.source(sourceBuilder);
  2. SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
  3. //7. 获取命中对象 SearchHits
  4. SearchHits hits = searchResponse.getHits();
  5. //7.1 获取总记录数
  6. Long total= hits.getTotalHits().value;
  7. System.out.println("总数:"+total);
  8. // aggregations 对象
  9. Aggregations aggregations = searchResponse.getAggregations();
  10. //将aggregations 转化为map
  11. Map<String, Aggregation> aggregationMap = aggregations.asMap();
  12. //通过key获取goods_brands 对象 使用Aggregation的子类接收 buckets属性在Terms接口中体现
  13. // Aggregation goods_brands1 = aggregationMap.get("goods");
  14. Terms goods_brands =(Terms) aggregationMap.get("goods");
  15. //获取buckets 数组集合
  16. List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();
  17. Map<String,Object>map=new HashMap<>();
  18. //遍历buckets key 属性名,doc_count 统计聚合数
  19. for (Terms.Bucket bucket : buckets) {
  20. System.out.println(bucket.getKey());
  21. map.put(bucket.getKeyAsString(),bucket.getDocCount());
  22. }
  23. System.out.println(map);

} ```