java-ElasticSearch详解

今天来学习一种基于智能搜索,分布式的搜索引擎ElasticSearch

1. 全文检索lucene

1.1 什么是全文检索

全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。

全文检索的方法主要分为按字检索和按词检索两种。按字检索是指对于文章中的每一个字都建立索引,检索时将词分解为字的组合。对于各种不同的语言而言,字有不同的含义,比如英文中字与词实际上是合一的,而中文中字与词有很大分别。按词检索指对文章中的词,即语义单位建立索引,检索时按词检索,并且可以处理同义项等。英文等西方文字由于按照空白切分词,因此实现上与按字处理类似,添加同义处理也很容易。中文等东方文字则需要切分字词,以达到按词索引的目的,关于这方面的问题,是当前全文检索技术尤其是中文全文检索技术中的难点,在此不做详述。

Lucene官方对自己的优势总结为几点:

  1. Scalable, High-Performance Indexing
  2. Powerful, Accurate and Efficient Search Algorithms

1.2 索引流程

image-20220112123627630

1.3 相关概念

  1. Index(索引)

    类似数据库的表的概念,但是与传统表的概念会有很大的不同。传统关系型数据库或者NoSQL数据库的表,在创建时至少要定义表的Scheme,定义表的主键或列等,会有一些明确定义的约束。而Lucene的Index,则完全没有约束。Lucene的Index可以理解为一个文档收纳箱,你可以往内部塞入新的文档,或者从里面拿出文档,但如果你要修改里面的某个文档,则必须先拿出来修改后再塞回去。这个收纳箱可以塞入各种类型的文档,文档里的内容可以任意定义,Lucene都能对其进行索引。

  2. Document(文档)

    类似数据库内的行或者文档数据库内的文档的概念,一个Index内会包含多个Document。写入Index的Document会被分配一个唯一的ID,即Sequence Number(更多被叫做DocId),关于Sequence Number后面会再细说。

  3. 索引库

    就是保存在磁盘上的一些列文件,里面存储了索引信息和文档

    image-20220112123800835

  4. Field(字段)

    一个Document会由一个或多个Field组成,Field是Lucene中数据索引的最小定义单位。Lucene提供多种不同类型的Field,例如StringField、TextField、LongFiled或NumericDocValuesField等,Lucene根据Field的类型(FieldType)来判断该数据要采用哪种类型的索引方式(Invert Index、Store Field、DocValues或N-dimensional等),关于Field和FieldType后面会再细说。

  5. Term和Term Dictionary

    Lucene中索引和搜索的最小单位,一个Field会由一个或多个Term组成,Term是由Field经过Analyzer(分词)产生。Term Dictionary即Term词典,是根据条件查找Term的基本索引。

2. ElasticSearch简介

ES=elaticsearch简写, Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

使用场景:

1)百度,谷歌,必应。我们可以通过他们去搜索我们需要的东西。但是我们的搜索不只是包含这些,还有京东站内搜索啊。

2)互联网的搜索:电商网站。招聘网站。新闻网站。各种APP(百度外卖,美团等等)

3)windows系统的搜索,OA软件,淘宝SSM网站,前后台的搜索功能

总结:搜索无处不在。通过一些关键字,给我们查询出来跟这些关键字相关的信息

Lucene与ES关系?

1)Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。

2)Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

ES主要解决问题:

1)检索相关数据;
2)返回统计结果;
3)速度要快。

3. ES相关术语

3.1 概述

ES和传统的关系型数据库的对应关系

1
2
mysql——>database——>tables——>rows——>cloumns
elasticsearch——>indices——>types——>documents——>fields

3.2 ES核心概念

3.2.1 index 索引(索引库)

我们为什么使用ES?因为想把数据存进去,然后再查询出来。

我们在使用Mysql或者Oracle的时候,为了区分数据,我们会建立不同的数据库,库下面还有表的。

其实ES功能就像一个关系型数据库,在这个数据库我们可以往里面添加数据,查询数据。

ES中的索引非传统索引的含义,ES中的索引是存放数据的地方,是ES中的一个概念词汇

index类似于我们Mysql里面的一个数据库 create database user; 好比就是一个索引库

3.2.2 type类型

类型是用来定义数据结构的

在每一个index下面,可以有一个或者多个type,好比数据库里面的一张表。

相当于表结构的描述,描述每个字段的类型。

3.2.3 Field 字段

好比关系型数据库中列的概念,一个document有一个或者多个field组成。

例如:

朝阳区:一个Mysql数据库

房子:create database chaoyaninfo

房间:create table people

3.2.4 Mapping 映射

处理数据映射的方式和规则方面做一些限制,如某个字段的数据类型,默认值,分析器,是否被索引等

3.2.5 document 文档

文档就是最终的数据了,可以认为一个文档就是一条记录。

是ES里面最小的数据单元,就好比表里面的一条数据

3.2.6 NRT(Near Realtime)近实时

img

3.2.7 cluster集群

ES是一个分布式的系统

ES直接解压不需要配置就可以使用,在hadoop1上解压一个ES,在hadoop2上解压了一个ES,接下来把这两个ES启动起来。他们就构成了一个集群。

在ES里面默认有一个配置,clustername 默认值就是ElasticSearch,如果这个值是一样的就属于同一个集群,不一样的值就是不一样的集群。

3.2.8 Node节点

就是集群中的一台服务器

3.2.9 shard:分片

一台服务器,无法存储大量的数据,ES把一个index里面的数据,分为多个shard,分布式的存储在各个服务器上面。

kafka:为什么支持分布式的功能,因为里面是有topic,支持分区的概念。所以topic A可以存在不同的节点上面。就可以支持海量数据和高并发,提升性能和吞吐量

3.2.10 replica:副本

一个分布式的集群,难免会有一台或者多台服务器宕机,如果我们没有副本这个概念。就会造成我们的shard发生故障,无法提供正常服务。

我们为了保证数据的安全,我们引入了replica的概念,跟hdfs里面的概念是一个意思。

可以保证我们数据的安全。

在ES集群中,我们一模一样的数据有多份,能正常提供查询和插入的分片我们叫做 primary shard,其余的我们就管他们叫做 replica shard(备份的分片)

当我们去查询数据的时候,我们数据是有备份的,它会同时发出命令让我们有数据的机器去查询结果,最后谁的查询结果快,我们就要谁的数据(这个不需要我们去控制,它内部就自己控制了)

3.3 总结

在默认情况下,我们创建一个库的时候,默认会帮我们创建5个主分片(primary shrad)和5个副分片(replica shard),所以说正常情况下是有10个分片的。

同一个节点上面,副本和主分片是一定不会在一台机器上面的,就是拥有相同数据的分片,是不会在同一个节点上面的。

所以当你有一个节点的时候,这个分片是不会把副本存在这仅有的一个节点上的,当你新加入了一台节点,ES会自动的给你在新机器上创建一个之前分片的副本。

4. ES集群的安装

1、设置max_map_count不能启动es会启动不起来

查看max_map_count的值 默认是65530

1
cat /proc/sys/vm/max_map_count

重新设置max_map_count的值

1
sysctl -w vm.max_map_count=262144

2、下载镜像并运行

1
2
3
4
5
#拉取镜像
docker pull elasticsearch:7.7.0

#启动镜像
docker run --name elasticsearch -d -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -e "discovery.type=single-node" -p 9200:9200 -p 9300:9300 elasticsearch:7.7.0

参数说明

—name表示镜像启动后的容器名称

-d: 后台运行容器,并返回容器ID;

-e: 指定容器内的环境变量

-p: 指定端口映射,格式为:主机(宿主)端口:容器端口

3、浏览器访问ip:9200 如果出现以下界面就是安装成功

![在这里插入图片描述](https://img-blog.csdnimg.cn/20201223171312149.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwOTQyNDkw,size_16,color_FFFFFF,t_70

4、开启远程连接

安装完成后,es并不能正常使用,5版本以后不支持自动远程连接,需要修改配置开启

配置跨域

通过项目中的配置,发现 cluster.name 这个与刚刚启动的不一致,ElasticSearch默认的cluster.name为elasticsearch,所以,这里需要进行修改。
首先进入容器。

1
docker exec -it es2 /bin/bash

img

我们需要对红框框圈中的文件”elasticsearch.yml”进行修改。]

img

这里使用vim和vi命令,都提示”没有发现这个命令”,这是因为Docker容器内部没有安装。所以,这里需要进行安装

1
2
apt-get update  # 获取最新的软件包
apt-get install vim # 下载

依次使用上面的命令即可安装成功,然后对”elasticsearch.yml”这个文件修改,增加如下内容。

cluster.name: “qfcwx-cluster”
network.host: 0.0.0.0
http.cors.enabled: true
http.cors.allow-origin: “*”

img

cluster.name:自定义集群名称。
network.host:当前es节点绑定的ip地址,默认127.0.0.1,如果需要开放对外访问这个属性必须设置。
http.cors.enabled:是否支持跨域,默认为false。
http.cors.allow-origin:当设置允许跨域,默认为*,表示支持所有域名,如果我们只是允许某些网站能访问,那么可以使用正则表达式。重启ElasticSearch容器

使用exit命令从容器内部退出。

注意,这里是重启ElasticSearch容器,并不是重新启动一个新的容器。

1
docker restart es2

使用浏览器进行访问:

img

5. ES的客户端操作

实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:
第一种,elasticsearch-head插件
第二种,使用elasticsearch提供的Restful接口直接访问(可以使用第三方工具,这里使用Postman工具)
第三种,使用elasticsearch提供的API进行访问

5.1 es-head管理界面

安装:

安装Head 插件

Elasticsearch Head Plugin: 对ES进行各种操作,如查询、删除、浏览索引等。

1、下载elasticsearch-head并解压

在线下载:wget https://github.com/mobz/elasticsearch-head/archive/master.zip

或者到github下载:https://github.com/mobz/elasticsearch-head

unzip elasticsearch-head-master.zip //解压zip文件

mv elasticsearch-head-master.zip /home/ntc/code/elasticsearch-head //解压到自定义目录并修改文件夹名为elasticsearch-head

2、安装node

由于head插件本质上还是一个nodejs的工程,因此需要安装node,使用npm来安装依赖的包。(npm可以理解为maven)

wget https://npm.taobao.org/mirrors/node/latest-v4.x/node-v4.4.7-linux-x64.tar.gz

tar -zxvf node-v4.4.7-linux-x64.tar.gz

解压完node的安装文件后,需要配置下环境变量,编辑/etc/profile,添加
export NODE_HOME=/export/servers/node-v4.4.7-linux-x64
export PATH=$NODE_HOME/bin:$PATH

保存后别忘记立即执行以下
source /etc/profile

这个时候可以测试一下node是否生效:node -v

5.2 postman进行restful访问

ElasticSearch的接口语法:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

image-20220112133911869

ES Restful API 中的VERB

image-20220112133937068

创建索引index和映射mapping

请求url:PUT localhost:9200/blog1
请求体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"mappings": {
"hello": {
"properties": {
"id": {
"type": "long",
"store": true,
"index":"not_analyzed"

},
"title": {
"type": "text",
"store": true,
"index":"analyzed",
"analyzer":"standard"

},
"content": {
"type": "text",
"store": true,
"index":"analyzed",
"analyzer":"standard"
}
}
}
}
}

通过head中的复合查询创建:

在这里插入图片描述

通过postman创建:

在这里插入图片描述

在head插件中查看索引信息:

在这里插入图片描述

创建索引后设置Mapping

直接使用put方法创建一个没有设置请求体的索引,然后设置mapping信息

请求的url:POST http://localhost:9200/blog2/hello/_mapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"hello": {
"properties": {
"id": {
"type": "long",
"store": true,
"index":"not_analyzed"

},
"title": {
"type": "text",
"store": true,
"index":"analyzed",
"analyzer":"standard"

},
"content": {
"type": "text",
"store": true,
"index":"analyzed",
"analyzer":"standard"
}
}
}
}

通过head插件中的复合查询创建:

在这里插入图片描述

通过postman创建

在这里插入图片描述

删除索引index

请求url:DELETE http://localhost:9200/blog1

直接在head插件中删除:

在这里插入图片描述

通过head插件符合查询删除:

在这里插入图片描述

通过postman删除:

在这里插入图片描述

创建文档document

请求url:POST http://localhost:9200/blog1/hello/1
请求体:

1
2
3
4
5
{
"id":1,
"title":"文档1",
"content":"文档1内容"
}

修改文档document

请求url:POST http://localhost:9200/blog1/hello/1
请求体:

1
2
3
4
5
{
"id":1,
"title":"修改后的文档1",
"content":"修改后的文档1内容"
}

删除文档document

请求url:DELETE http://localhost:9200/blog1/hello/1

查询文档

查询文档-根据id查询

查询文档-term查询

对搜索的关键词不分词

请求url:POST http://localhost:9200/blog1/hello/_search

请求体:

1
2
3
4
5
6
7
{
"query": {
"term": {
"title": "文"
}
}
}

查询文档-querystring查询

搜索之前对搜索的关键词分词

请求url:POST http://localhost:9200/blog1/hello/_search
请求体:

1
2
3
4
5
6
7
8
{
"query": {
"query_string": {
"default_field": "title",
"query": "文档"
}
}
}

6. IK中文分词器

IKAnalyzer是一个开源的,基于java开发的轻量级的中文分词工具包。ES默认没有携带IK分词器,需要下载,下载地址:
https://github.com/medcl/elasticsearch-analysis-ik/releases

6.1 下载安装

1.下载IK压缩包,本文使用ES是7.3.0,下载的IK也是7.3.0;
2.解压放到es安装目录中的plugin目录中,随便创建的文件目录

在这里插入图片描述

重启es即可

6.2 测试效果

IK分词器,支持两种算法。分别为:

  • ik_smart :最少切分
  • ik_max_word :最细粒度切分

下面看效果,还是测试 “正在学习elastic search” 这个字符串。

1.ik_smart在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"tokens": [
{
"token": "正在",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "学习",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "elastic",
"start_offset": 4,
"end_offset": 11,
"type": "ENGLISH",
"position": 2
},
{
"token": "serach",
"start_offset": 12,
"end_offset": 18,
"type": "ENGLISH",
"position": 3
}
]
}

2.ik_max_word

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"tokens": [
{
"token": "正在",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "在学",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 1
},
{
"token": "学习",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
},
{
"token": "elastic",
"start_offset": 4,
"end_offset": 11,
"type": "ENGLISH",
"position": 3
},
{
"token": "serach",
"start_offset": 12,
"end_offset": 18,
"type": "ENGLISH",
"position": 4
}
]
}

对比结果,就能看出ik_max_word和 ik_smart 算法的区别,ik_max_word分出的词数更多更细一些。

7. kibana

Kibana 的版本需要和 Elasticsearch 的版本一致。这是官方支持的配置。

运行不同主版本号的 Kibana 和 Elasticsearch 是不支持的(例如 Kibana 5.x 和 Elasticsearch 2.x),若主版本号相同,运行 Kibana 子版本号比 Elasticsearch 子版本号新的版本也是不支持的(例如 Kibana 5.1 和 Elasticsearch 5.0)。

运行一个 Elasticsearch 子版本号大于 Kibana 的版本基本不会有问题,这种情况一般是便于先将 Elasticsearch 升级(例如 Kibana 5.0 和 Elasticsearch 5.1)。在这种配置下,Kibana 启动日志中会出现一个警告,所以一般只是使用于 Kibana 即将要升级到和 Elasticsearch 相同版本的场景。

运行不同的 Kibana 和 Elasticsearch 补丁版本一般是支持的(例如:Kibana 5.0.0 和 Elasticsearch 5.0.1)。

7.1 下载链接

下载链接
1、官网链接:

https://www.elastic.co/guide/en/kibana/5.2/targz.html

2、其他安装方式:https://www.elastic.co/guide/cn/kibana/current/install.html

例如有:

a、 https://www.elastic.co/guide/cn/kibana/current/deb.html

deb 包用来在 Debian、Ubuntu 和其他基于 Debian 的系统下安装,Debian 包可以从 Elastic 官网或者我们的 Debian 仓库中下载。

b、elastic.co/guide/cn/kibana/current/rpm.html

rpm 包用来在 Red Hat、Centos、SLES、OpenSuSe 以及其他基于 RPM 的系统下安装。RPM 包可以从 Elastic 官网或者我们的 RPM 仓库下载。

c、https://www.elastic.co/guide/cn/kibana/current/docker.html

Elastic Docker 仓库中有现有的可以运行 Kibana 的 Docker 镜像,并预装了 X-Pack 。

7.2 下载及安装

1.下载安装包

[admin@admin01 modules]wgethttps://artifacts.elastic.co/downloads/kibana/kibana−5.2.2−linux−x8664.tar.gz[admin@admin01modules] wget https://artifacts.elastic.co/downloads/kibana/kibana-5.2.2-linux-x86_64.tar.gz
[admin@admin01 modules] tar -xzf kibana-5.2.2-linux-x86_64.tar.gz
[admin@admin01 modules]$ cd ../softwares/kibana-5.2.2-linux-x86_64/

2.配置相关的信息

[admin@admin01 kibana-5.2.2-linux-x86_64]$ vi config/kibana.yml
    server.port: 5601
    server.host: 192.168.47.101
    elasticsearch.url: "http://192.168.47.101:9200"

3.外部访问,添加防火墙端口 5601

 [root@admin01 ~]# iptables -N RH-Firewall-1-INPUT
    [root@admin01 ~]# service iptables save
    iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]
    [root@admin01 ~]# vi /etc/sysconfig/iptables
    -ARH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 5601 -j ACCEPT  # 添加到该目录下
    [root@admin01 ~]# service iptables restart               # 重新启动端口
    iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
    iptables: Flushing firewall rules:                         [  OK  ]
    iptables: Unloading modules:                               [  OK  ]
    iptables: Applying firewall rules:                         [  OK  ]

4.启动

[admin@admin01 softwares]cdkibana−5.2.2−linux−x8664/[admin@admin01kibana−5.2.2−linux−x8664] cd kibana-5.2.2-linux-x86_64/
[admin@admin01 kibana-5.2.2-linux-x86_64] bin/kibana

验证 Kibana是否正常启动,则通过之前配置的端口信息,则可以进行访问,具体的访问如下所示:
在这里插入图片描述

7.3 DSL语句使用

查看集群

查看集群健康信息

GET /_cat/health?v

在这里插入图片描述

集群状态(status)

Green(正常)
Yellow(正常,但是一些副本还没有分配)
Red(非正常)

可以使用GET /_cat/health?help查看每个操作返回结果字段的意义

注意:

    这里的GET是RESTful API的请求方式
    /_cat/health?help 是RESTful API接口
    你也可以使用PostMan这样的RESTful API测试工具,但是没有提示

查看每个操作返回结果字段的意义

GET /_cat/health?help

注意:

  1. 这里的GET是RESTful API的请求方式
  2. /_cat/health?help 是RESTful API接口
  3. 你也可以使用PostMan这样的RESTful API测试工具,但是没有提示

在这里插入图片描述

查看集群中节点信息

GET /_cat/nodes?v

ip             heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.23.141            9          91   7    0.10    0.08     0.13 mdi       *      x0vIhEF

查看集群中的索引信息

GET /_cat/indices?v

health status index  uuid                   pri rep docs.count docs.deleted store.size 
yellow open   baizhi BYzhTHMzQIKEiyaKXklueQ   5   1          0            0      1.2kb

简化写法

GET /_cat/indices?v&h=health,status,index

1
2
health status index
yellow open baizhi

索引操作

创建索引

PUT /baizhi

1
2
3
4
5
6
#! Deprecation: the default number of shards will change from [5] to [1] in 7.0.0; if you wish to continue using the default of [5] shards, you must manage this on the create index request or with an index template
{
"acknowledged": true, # 创建成功返回true
"shards_acknowledged": true,
"index": "baizhi"
}

上面的操作使用默认的配置信息创建一个索引

删除索引

DELETE /baizhi

{
  "acknowledged": true
}

创建类型Mapping

PUT /baizhi # 创建index(baizhi)并添加类型mapping(doc)

{
  "mappings": {
    "doc": { 
      "properties": { 
        "title":    { "type": "text"  },  # 注意: 字符串常用类型:text类型会分词 keyword类型不会分词
        "name":     { "type": "text"  }, 
        "age":      { "type": "integer" },  
        "created":  {
          "type":   "date", 
          "format": "strict_date_optional_time||epoch_millis"
        }
      }
    }
  }
}

POST /baizhi/user # 创建index(baizhi)后,在指定index中添加类型mapping(user)

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"user": {
"properties": {
"id": { "type": "text" },
"name": { "type": "text" },
"age": { "type": "integer" },
"created": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
}
}
}
}

Mapping Type:

  1. 简单类型: text, keyword, date, long, double, boolean or ip
  2. 其它类型: object, geo_point, geo_shape

查看类型mapping

GET /baizhi/_mapping/_doc # 语法:GET /索引名/_mapping/类型名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-------------------------------------------
{
"baizhi": {
"mappings": {
"_doc": {
"properties": {
"age": {
"type": "integer"
},
"created": {
"type": "date"
},
"name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
}
}

文档操作

新增单个文档

PUT /baizhi/_doc/1 # put /索引名/类型名/id

1
2
3
4
5
6
7

{ # request body
"name":"zs",
"title":"张三",
"age":18,
"created":"2018-12-25"
}

POST /baizhi/_doc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

{
"name":"ls",
"title":"李四",
"age":28,
"created":"2018-12-26"
}
-------------------------------------------------------------
{
"_index": "baizhi",
"_type": "_doc",
"_id": "KbOj6GcBVEuCC3JSh18Y", # ES自动生成的文档的id
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}

查询单个文档

GET /baizhi/_doc/1 # 语法: GET /索引名/类型名/id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

-------------------------------------------------------------------
{
"_index": "baizhi",
"_type": "_doc",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "zs",
"title": "张三",
"age": 18,
"created": "2018-12-25"
}
}

GET /baizhi/_doc/KbOj6GcBVEuCC3JSh18Y
--------------------------------------------------------------------
{
"_index": "baizhi",
"_type": "_doc",
"_id": "KbOj6GcBVEuCC3JSh18Y",
"_version": 1,
"found": true,
"_source": {
"name": "ls",
"title": "李四",
"age": 28,
"created": "2018-12-26"
}
}

修改单个文档

PUT /baizhi/_doc/KbOj6GcBVEuCC3JSh18Y # 语法: PUT /索引名/类型名/id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

{
"name":"lxs",
"title":"李小四"
}
--------------------------------------------------------------------
{
"_index": "baizhi",
"_type": "_doc",
"_id": "KbOj6GcBVEuCC3JSh18Y",
"_version": 2,
"found": true,
"_source": {
"name": "lxs",
"title": "李小四"
}
}

删除单个文档

DELETE /baizhi/_doc/1 # 语法: DELETE /索引名/类型名/id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


--------------------------------------------------------------------
{
"_index": "baizhi",
"_type": "_doc",
"_id": "1",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}

批处理操作

除了能够索引、更新和删除单个文档外,Elasticsearch还提供了使用_bulk API批量执行上述任何操作的能力。这个功能非常重要,因为它提供了一种非常有效的机制,可以以尽可能少的网络往返尽可能快地执行多个操作

POST /baizhi/_doc/_bulk # 批量插入多个document

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

{"index":{}}
{"name":"ww","title":"王五","age":18,"created":"2018-12-27"}
{"index":{}}
{"name":"zl","title":"赵六","age":25,"created":"2018-12-27"}
-------------------------------------------------------------------
{
"took": 65,
"errors": false, # 批量插入成功
"items": [
{
"index": {
"_index": "baizhi",
"_type": "_doc",
"_id": "KrOP6WcBVEuCC3JS8V9K",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "baizhi",
"_type": "_doc",
"_id": "K7OP6WcBVEuCC3JS8V9K",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
}
]
}
1
2
3
4
POST /baizhi/_doc/_bulk	 # 批量操作(包含修改和删除)
{"update":{"_id":"KrOP6WcBVEuCC3JS8V9K"}} # 修改
{"doc":{"title":"王小五"}}
{"delete":{"_id":"K7OP6WcBVEuCC3JS8V9K"}} # 删除

8. java api

1、导入pom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>


<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

2、创建客户端

1
2
3
4
5
6
private TransportClient client;
@BeforeEach
public void test1() throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", "myes").build();
client = new PreBuiltTransportClient(settings).addTransportAddress(new TransportAddress(InetAddress.getByName("node01"),9300)).addTransportAddress(new TransportAddress(InetAddress.getByName("node02"),9300));
}

3、XcontentBuilder实现创建索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 通过XContentBuilder来实现索引的创建
* @throws IOException
*/
@Test
public void index3() throws IOException {
IndexResponse indexResponse = client.prepareIndex("myindex1", "article", "3")
.setSource(new XContentFactory().jsonBuilder()
.startObject()
.field("name", "lisi")
.field("age", "18")
.field("sex", "0")
.field("address", "bj")
.endObject())
.get();
client.close();

}

创建document对象

1、通过XContentBuilder创建文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
//创建文档(通过XContentBuilder)
publicvoidtest4() throwsException{
// 创建Client连接对象
Settings settings=Settings.builder().put("cluster.name", "my‐elasticsearch").build();
TransportClient client=new PreBuiltTransportClient(settings) .addTransportAddress(newInetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
//创建文档信息
XContentBuilder builder=XContentFactory.jsonBuilder()
.startObject()
.field("id", 1)
.field("title", "ElasticSearch是一个基于Lucene的搜索服务器")
.field("content","它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。")
.endObject();
// 建立文档对象
/**
* 参数一blog1:表示索引对象
* 参数二article:类型
* 参数三1:建立id
*/
client.prepareIndex("blog2", "article", "1").setSource(builder).get();
//释放资源
client.close();
}

2、通过使用Jackson转换实体创建文档

  1. 创建实体类对象

    1
    2
    3
    4
    5
    6
    @Data
    public class Article {
    private Integer id;
    private String title;
    private String content;
    }
  2. 添加jackson依赖包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson‐core</artifactId>
    <version>2.8.1</version>
    </dependency>
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson‐databind</artifactId>
    <version>2.8.1</version>
    </dependency>
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson‐annotations</artifactId>
    <version>2.8.1</version>
    </dependency>
  3. 代码具体实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Test
    //创建文档(通过实体转json)
    publicvoidtest5() throwsException{
    // 创建Client连接对象
    Settings settings=Settings.builder().put("cluster.name", "my‐elasticsearch").build();
    TransportClient client=new PreBuiltTransportClient(settings) .addTransportAddress(newInetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));

    // 描述json 数据
    //{id:xxx, title:xxx,content:xxx}
    Article article=new Article();
    article.setId(2);
    article.setTitle("搜索工作其实很快乐");
    article.setContent("我们希望我们的搜索解决方案要快,我们希望有一个零配置和一个完全免费的搜索模式,我们希望能够简单地使用JSON通过HTTP的索引数据,我们希望我们的搜索服务器始终可用,我们希望能够一台开始并扩展到数百,我们要实时搜索,我们要简单的多租户,我们希望建立一个云的解决方案。Elasticsearch旨在解决所有这些问题和更多的问题。");

    ObjectMapper objectMapper=new ObjectMapper();

    // 建立文档
    client.prepareIndex("blog2", "article", article.getId().toString()) .setSource(objectMapper.writeValueAsString(article).getBytes(),XContentType.JSON).get();
    //释放资源
    client.close();
    }

3、查询文档操作

  1. termQuery词条查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 词条查询
    */
    @Test
    public void termQuery(){
    SearchResponse searchResponse = client.prepareSearch("indexsearch").setTypes("mysearch")
    .setQuery(new TermQueryBuilder("say", "熟悉"))
    .get();
    SearchHits hits = searchResponse.getHits();
    SearchHit[] hits1 = hits.getHits();
    for (SearchHit documentFields : hits1) {
    System.out.println(documentFields.getSourceAsString());
    }
    }
  1. 分页查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /*分页查询
    */
    @Test
    public void getPageIndex(){
    int pageSize = 5;
    int pageNum = 2;
    int startNum = (pageNum-1)*5;
    SearchResponse searchResponse = client.prepareSearch("indexsearch")
    .setTypes("mysearch")
    .setQuery(QueryBuilders.matchAllQuery())
    .addSort("id",SortOrder.ASC)
    .setFrom(startNum)
    .setSize(pageSize)
    .get();
    SearchHits hits = searchResponse.getHits();
    SearchHit[] hits1 = hits.getHits();
    for (SearchHit documentFields : hits1) {
    System.out.println(documentFields.getSourceAsString());
    }
    client.close();
    }
  2. 高亮查询

    在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮

    京东商城搜索”笔记本”
    高亮显示的html分析

    通过开发者工具查看高亮数据的html代码实现:

    ElasticSearch可以对查询出的内容中关键字部分进行标签和样式的设置,但是你需要告诉ElasticSearch使用什么标签对高亮关键字进行包裹

  3. 高亮显示代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /**
    * 高亮查询
    */
    @Test
    public void highLight(){
    //设置我们的查询高亮字段
    SearchRequestBuilder searchRequestBuilder = client.prepareSearch("indexsearch")
    .setTypes("mysearch")
    .setQuery(QueryBuilders.termQuery("say", "hello"));

    //设置我们字段高亮的前缀与后缀
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("say").preTags("<font style='color:red'>").postTags("</font>");

    //通过高亮来进行我们的数据查询
    SearchResponse searchResponse = searchRequestBuilder.highlighter(highlightBuilder).get();
    SearchHits hits = searchResponse.getHits();
    System.out.println("查询出来一共"+ hits.totalHits+"条数据");
    for (SearchHit hit : hits) {
    //打印没有高亮显示的数据
    System.out.println(hit.getSourceAsString());
    System.out.println("=========================");
    //打印我们经过高亮显示之后的数据
    Text[] says = hit.getHighlightFields().get("say").getFragments();
    for (Text say : says) {
    System.out.println(say);
    }

    /* Map<String, HighlightField> highlightFields = hit.getHighlightFields();
    System.out.println(highlightFields);*/
    }
    client.close();
    }

9. Spring Data Elasticsearch

Elasticsearch提供的Java客户端有一些不太方便的地方:

很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的
需要自己把对象序列化为json存储
查询到结果也需要自己反序列化为对象

因此,我们这里就不讲解原生的Elasticsearch客户端API了。

而是学习Spring提供的套件:Spring Data ElasticsearchSpring Data 是的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率。

9.1 实例

  1. 创建Demo工程

    我们新建一个demo,学习Elasticsearch

    pom依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.czxy</groupId>
    <artifactId>bos-es</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>bos-es</name>
    <description>Demo project for Spring Boot</description>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    application.yml文件配置:

    1
    2
    3
    4
    5
    spring:
    data:
    elasticsearch:
    cluster-name: my-application
    cluster-nodes: 127.0.0.1:9300 # 程序连接es的端口号是9300

    image-20220112170433795

索引操作

创建索引和映射

SpringBoot-data-elasticsearch提供了面向对象的方式操作elasticsearch

业务:将商品的信息存入elasticsearch,并且执行搜索操作

创建一个商品对象,有这些属性:id 编号,title 标题,category 分类,brand 品牌,price 价格, 图片地址

在SpringDataElasticSearch中,只需要操作对象,就可以操作elasticsearch中的数据

实体类

首先我们准备好实体类:

public class Item {
    private Long id;
    private String title; //标题
    private String category;// 分类
    private String brand; // 品牌
    private Double price; // 价格
    private String images; // 图片地址
}

映射—注解
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:

@Document 作用在类,标记实体类为文档对象,一般有两个属性
    indexName:对应索引库名称
    type:对应在索引库中的类型
    shards:分片数量,默认5
    replicas:副本数量,默认1
@Id 作用在成员变量,标记一个字段作为id主键
@Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
    type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等
        text:存储数据时候,会自动分词,并生成索引
        keyword:存储数据时候,不会分词建立索引
        Numerical:数值类型,分两类
            基本数据类型:long、interger、short、byte、double、float、half_float
            浮点数的高精度类型:scaled_float
                需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
        Date:日期类型
            elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
    index:是否索引,布尔类型,默认是true
    store:是否存储,布尔类型,默认是false
    analyzer:分词器名称,这里的ik_max_word即使用ik分词器

创建索引

ElasticsearchTemplate中提供了创建索引的API:

在这里插入图片描述

映射

映射相关的API:

在这里插入图片描述

一样,可以根据类的字节码信息(注解配置)来生成映射,或者手动编写映射

我们这里采用类的字节码信息创建索引并映射:

@Test
public void createIndex() {
    // 创建索引,会根据Item类的@Document注解信息来创建
    esTemplate.createIndex(Item.class);
    // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
    esTemplate.putMapping(Item.class);
}

索引信息:
在这里插入图片描述

删除索引

删除索引的API:

在这里插入图片描述

可以根据类名或索引名删除。

示例:

@Test
public void deleteIndex() {
esTemplate.deleteIndex(Item.class);
// 根据索引名字删除
//esTemplate.deleteIndex(“item1”);
}

1
2
3
4
5
6

结果:OK

新增文档数据

Repository接口

Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。

来看下Repository的继承关系:

image-20220112171003770

我们看到有一个ElasticsearchCrudRepository接口:

image-20220112171040915

所以,我们只需要定义接口,然后继承它就OK了。

1
2
public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
}

接下来,我们测试新增数据:

新增一个对象

1
2
3
4
5
6
7
8
9
@Autowired
private ItemRepository itemRepository;

@Test
public void index() {undefined
Item item = new Item(1L, “小米手机7”, " 手机",
“小米”, 3499.00, “http://image.baidu.com/13123.jpg”);
itemRepository.save(item);
}

去页面查询看看:

image-20220112171237175

批量新增

代码:

@Test
public void indexList() {
    List<Item> list = new ArrayList<>();
    list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.baidu.com/13123.jpg"));
    list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.baidu.com/13123.jpg"));
    // 接收对象集合,实现批量新增
    itemRepository.saveAll(list);
}

再次去页面查询:

image-20220112171320892

修改

elasticsearch中本没有修改,它是先删除再新增

修改和新增是同一个接口,区分的依据就是id。

@Test
public void index(){
    Item item = new Item(1L, "苹果XSMax", " 手机",
            "小米", 3499.00, "http://image.baidu.com/13123.jpg");
    itemRepository.save(item);
}

查看结果:

image-20220112171528152

查询

ElasticsearchRepository提供了一些基本的查询方法:

image-20220112171559122

基本查询

ElasticsearchRepository提供了一些基本的查询方法:

image-20220112171623538

我们来试试查询所有:

@Test
    public void testFindAll(){
        //1 查找所有
//        Iterable<Item> item = itemRepository.findAll();
//        Iterator<Item> it = item.iterator();
//        while (it.hasNext()){
//            System.out.println(it.next());
//        }
        //2 分页查找
//        Page<Item> page = itemRepository.findAll(PageRequest.of(1, 5));
//
//        for(Item item:page){
//            System.out.println(item);
//        }   
   //3 排序
    Iterable<Item> iterable = itemRepository.findAll(Sort.by("price").descending());
    Iterator<Item> it = iterable.iterator();
    while(it.hasNext()){
        System.out.println(it.next());
    }
}

match query:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void search(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));
// 搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("total = " + total);
for (Item item : items) {
System.out.println(item);
}
}

NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体
QueryBuilders.matchQuery(“title”, “小米手机”):利用QueryBuilders来生成一个查询。QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询:

image-20220112171815250

Page:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:

  • totalElements:总条数
  • totalPages:总页数
  • Iterator:迭代器,本身实现了Iterator接口,因此可直接迭代得到当前页的数据
  • 其它属性:

image-20220112171840310

结果:

在这里插入图片描述

termQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void testTermQuery(){

// 查询条件生成器
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.termQuery("title","小米"));

// 查询 自动分页 ,默认查找第一页的10条数据
Page<Item> list = this.itemRepository.search(builder.build());

for(Item item:list){
System.out.println(item);
}


}

9.2 测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* match底层是词条匹配
*/
@Test
public void testMathcQuery(){
// 查询条件生成器
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.matchQuery("title","小米"));

// 查询 自动分页 ,默认查找第一页的10条数据
Page<Item> list = this.itemRepository.search(builder.build());

for(Item item:list){
System.out.println(item);
}

}


@Test
public void testTermQuery(){

// 查询条件生成器
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.termQuery("title","小米"));

// 查询 自动分页 ,默认查找第一页的10条数据
Page<Item> list = this.itemRepository.search(builder.build());

for(Item item:list){
System.out.println(item);
}


}


@Test
public void testFuzzyQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.fuzzyQuery("title","faceoooo"));

Page<Item> list = this.itemRepository.search(builder.build());
for(Item item:list){
System.out.println(item);
}


}


@Test
public void testBooleanQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("title","手机"))
.must(QueryBuilders.termQuery("brand","小米"))
);

Page<Item> list = this.itemRepository.search(builder.build());
for(Item item:list){
System.out.println(item);
}


}
@Test
public void testRangeQuery(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// queryBuilder.withQuery(QueryBuilders.fuzzyQuery("title","小目"));


queryBuilder.withQuery(QueryBuilders.rangeQuery("price").from(3000).to(4000));

Page<Item> page = itemRespository.search(queryBuilder.build());

for(Item i:page){
System.out.println(i);
}


}

9.3 聚合查询

概念

ES聚合分析是什么?

概念 Elasticsearch除全文检索功能外提供的针对Elasticsearch数据做统计分析的功能。它的实时性高,所有的计算结果都是即时返回。
Elasticsearch将聚合分析主要分为如下4类:

Metric(指标): 指标分析类型,如计算最大值、最小值、平均值等等 (对桶内的文档进行聚合分析的操作)
Bucket(桶): 分桶类型,类似SQL中的GROUP BY语法 (满足特定条件的文档的集合)
Pipeline(管道): 管道分析类型,基于上一级的聚合分析结果进行在分析
Matrix(矩阵): 矩阵分析类型(聚合是一种面向数值型的聚合,用于计算一组文档字段中的统计信息)

ES聚合分析查询的写法

在查询请求体中以aggregations节点按如下语法定义聚合分析:

1
2
3
4
5
6
7
8
9
10
"aggregations" : {
"<aggregation_name>" : { <!--聚合的名字 -->
"<aggregation_type>" : { <!--聚合的类型 -->
<aggregation_body> <!--聚合体:对哪些字段进行聚合 -->
}
[,"meta" : { [<meta_data_body>] } ]? <!--元 -->
[,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 -->
}
[,"<aggregation_name_2>" : { ... } ]* <!--聚合的名字 -->
}

指标(metric)和 桶(bucket)

虽然Elasticsearch有四种聚合方式,但在一般实际开发中,用到的比较多的就是Metric和Bucket。

(1) 桶(bucket)  

  a、简单来说桶就是满足特定条件的文档的集合。

  b、当聚合开始被执行,每个文档里面的值通过计算来决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着开始聚合操作。

  c、桶也可以被嵌套在其他桶里面。

(2)指标(metric)

  a、桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。分桶是一种达到目的地的手段:它提供了一种给文档分组的方法来让

我们可以计算感兴趣的指标。

  b、大多数指标是简单的数学运算(如:最小值、平均值、最大值、汇总),这些是通过文档的值来计算的。