课程介绍  
Elastic Stack简介  
Elasticsearch的介绍与安装  
Elasticsearch的快速入门  
Elasticsearch的核心讲解  
中文分词  
全文搜索  
Elasticsearch集群  
Java客户端讲解  
1Elastic Stack简介  
如果你没有听说过Elastic Stack,那你一定听说过ELK,实际上ELK是三款软件的简称,分别是Elasticsearch、  
LogstashKibana组成,在发展的过程中,又有新成员Beats的加入,所以就形成了Elastic Stack。所以说,ELK是  
旧的称呼,Elastic Stack是新的名字。  
全系的Elastic Stack技术栈包括:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
Elasticsearch  
Elasticsearch 基于java,是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引  
副本机制,restful风格接口,多数据源,自动搜索负载等。  
Logstash  
Logstash 基于java,是一个开源的用于收集,分析和存储日志的工具。  
Kibana  
Kibana 基于nodejs,也是一个开源和免费的工具,Kibana可以为 Logstash ElasticSearch 提供的日志分析友好的  
Web 界面,可以汇总、分析和搜索重要数据日志。  
Beats  
Beatselastic公司开源的一款采集系统监控数据的代理agent,是在被监控服务器上以客户端形式运行的数据收集  
器的统称,可以直接把数据发送给Elasticsearch或者通过Logstash发送给Elasticsearch,然后进行后续的数据分析活  
动。  
Beats由如下组成:  
Packetbeat:是一个网络数据包分析器,用于监控、收集网络流量信息,Packetbeat嗅探服务器之间的流量,  
解析应用层协议,并关联到消息的处理,其支 持ICMP (v4 and v6)DNSHTTPMysqlPostgreSQL、  
RedisMongoDBMemcache等协议;  
Filebeat:用于监控、收集服务器日志文件,其已取代 logstash forwarder;  
Metricbeat:可定期获取外部系统的监控指标信息,其可以监控、收集 ApacheHAProxyMongoDB  
MySQLNginxPostgreSQLRedisSystemZookeeper等服务;  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
Winlogbeat:用于监控、收集Windows系统的日志信息;  
2Elasticsearch  
2
.1、简介  
2
.2、安装  
2
.2.1、版本说明  
Elasticsearch的发展是非常快速的,所以在ES5.0之前,ELK的各个版本都不统一,出现了版本号混乱的状态,所以  
5.0开始,所有Elastic Stack中的项目全部统一版本号。目前最新版本是6.5.4,我们将基于这一版本进行学习。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
2
.2.2、下载  
或者,使用资料中提供的已下载好的安装包。  
2
.2.3、单机版安装  
1
2
3
4
5
#创建elsearch用户,Elasticsearch不支持root用户运行  
useradd elsearch  
#解压安装包  
tar -xvf elasticsearch-6.5.4.tar.gz -C /itcast/es/  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
6
7
8
9
0
#修改配置文件  
vim conf/elasticsearch.yml  
network.host: 0.0.0.0 ꢀ#设置ip地址,任意网络均可访问  
1
1
1 #说明:在Elasticsearch中如果,network.host不是localhost或者127.0.0.1的话,就会认为是生产环境,  
会对环境的要求比较高,我们的测试环境不一定能够满足,一般情况下需要修改2处配置,如下:  
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
5
5
2 #1:修改jvm启动参数  
3 vim conf/jvm.options  
4 -Xms128m #根据自己机器情况修改  
5 -Xmx128m  
6 #2:一个进程在VMAs(虚拟内存区域)创建内存映射最大数量  
7 vim /etc/sysctl.conf  
8 vm.max_map_count=655360  
9
0 sysctl -p #配置生效  
1
2 #启动ES服务  
3 su - elsearch  
4 cd bin  
5 ./elasticsearch ./elasticsearch -d #后台启动  
6
7 #通过访问进行测试,看到如下信息,就说明ES启动成功了  
8 {  
9
ꢀ ꢀ"name": "dSQV6I8",  
0
ꢀ ꢀ"cluster_name": "elasticsearch",  
ꢀ ꢀ"cluster_uuid": "v5GPTWAtT5emxFdjigFg-w",  
ꢀ ꢀ"version": {  
1
2
3
ꢀ ꢀ ꢀ ꢀ"number": "6.5.4",  
4
ꢀ ꢀ ꢀ ꢀ"build_flavor": "default",  
ꢀ ꢀ ꢀ ꢀ"build_type": "tar",  
5
6
ꢀ ꢀ ꢀ ꢀ"build_hash": "d2ef93d",  
ꢀ ꢀ ꢀ ꢀ"build_date": "2018-12-17T21:17:40.758843Z",  
ꢀ ꢀ ꢀ ꢀ"build_snapshot": false,  
ꢀ ꢀ ꢀ ꢀ"lucene_version": "7.5.0",  
ꢀ ꢀ ꢀ ꢀ"minimum_wire_compatibility_version": "5.6.0",  
ꢀ ꢀ ꢀ ꢀ"minimum_index_compatibility_version": "5.0.0"  
ꢀ },  
7
8
9
0
1
2
3
ꢀ ꢀ"tagline": "You Know, for Search"  
4 }  
5
6 #停止服务  
7 root@itcast:~# jps  
8 68709 Jps  
9 68072 Elasticsearch  
0
1 kill 68072#通过kill结束进程  
1
2
#启动出错,环境:Centos6  
[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at  
least [65536]  
3
4
#解决:切换到root用户,编辑limits.conf 添加类似如下内容  
vi /etc/security/limits.conf  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
5
6
7
8
9
0
1
添加如下内容:  
* soft nofile 65536  
* hard nofile 131072  
* soft nproc 2048  
* hard nproc 4096  
1
1
1
2 [2]: max number of threads [1024] for user [elsearch] is too low, increase to at least  
4096]  
[
1
1
1
1
1
1
1
2
3 #解决:切换到root用户,进入limits.d目录下修改配置文件。  
4 vi /etc/security/limits.d/90-nproc.conf  
5 #修改如下内容:  
6 * soft nproc 1024  
7 #修改为  
8 * soft nproc 4096  
9
0 [3]: system call filters failed to install; check the logs and fix your configuration  
or disable system call filters at your own risk  
1
2
2
2
2
2
2 #解决:Centos6不支持SecComp,而ES5.2.0默认bootstrap.system_call_filtertrue  
3 vim config/elasticsearch.yml  
4
添加:  
5 bootstrap.system_call_filter: false  
2
.2.4elasticsearch-head  
面客户端工具,其源码托管于GitHub,地址为:https://github.com/mobz/elasticsearch-head  
head提供了4种安装方式:  
源码安装,通过npm run start启动(不推荐)  
通过docker安装(推荐)  
通过chrome插件安装(推荐)  
通过ESplugin方式安装(不推荐)  
通过docker安装  
1
2
3
4
5
6
7
8
#拉取镜像  
docker pull mobz/elasticsearch-head:5  
#创建容器  
docker create --name elasticsearch-head -p 9100:9100 mobz/elasticsearch-head:5  
#启动容器  
docker start elasticsearch-head  
通过浏览器进行访问:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
注意:  
由于前后端分离开发,所以会存在跨域问题,需要在服务端做CORS的配置,如下:  
vim elasticsearch.yml  
http.cors.enabled: true http.cors.allow-origin: "*"  
chrome  
chrome插件的方式安装  
建议:推荐使用chrome插件的方式安装,如果网络环境不允许,就采用其它方式安装。  
2
.3、基本概念  
索引  
索引(index)是Elasticsearch对逻辑数据的逻辑存储,所以它可以分为更小的部分。  
可以把索引看成关系型数据库的表,索引的结构是为快速有效的全文索引准备的,特别是它不存储原始值。  
Elasticsearch可以把索引存放在一台机器或者分散在多台服务器上,每个索引有一或多个分片(shard),每个  
分片可以有多个副本(replica)。  
文档  
存储在Elasticsearch中的主要实体叫文档(document)。用关系型数据库来类比的话,一个文档相当于数据库  
表中的一行记录。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
ElasticsearchMongoDB中的文档类似,都可以有不同的结构,但Elasticsearch的文档中,相同字段必须有相  
同类型。  
文档由多个字段组成,每个字段可能多次出现在一个文档里,这样的字段叫多值字段(multivalued)。  
每个字段的类型,可以是文本、数值、日期等。字段类型也可以是复杂类型,一个字段包含其他子文档或者数  
组。  
映射  
所有文档写进索引之前都会先进行分析,如何将输入的文本分割为词条、哪些词条又会被过滤,这种行为叫做  
映射(mapping)。一般由用户自己定义规则。  
文档类型  
Elasticsearch中,一个索引对象可以存储很多不同用途的对象。例如,一个博客应用程序可以保存文章和评  
论。  
每个文档可以有不同的结构。  
不同的文档类型不能为相同的属性设置不同的类型。例如,在同一索引中的所有文档类型中,一个叫title的字段  
必须具有相同的类型。  
2
.4RESTful API  
Elasticsearch中,提供了功能丰富的RESTful API的操作,包括基本的CRUD、创建索引、删除索引等操作。  
2
.4.1、创建非结构化索引  
Lucene中,创建索引是需要定义字段名称以及字段的类型的,在Elasticsearch中提供了非结构化的索引,就是不  
需要创建索引结构,即可写入数据到索引中,实际上在Elasticsearch底层会进行结构化操作,此操作对用户是透明  
的。  
创建空索引:  
1
2
3
4
5
6
7
8
9
PUT /haoke  
{
"settings": {  
ꢀ ꢀ ꢀ"index": {  
ꢀ ꢀ ꢀ ꢀ "number_of_shards": "2", #分片数  
ꢀ ꢀ ꢀ ꢀ "number_of_replicas": "0" #副本数  
ꢀ ꢀ }  
ꢀ }  
1
1
1
1
1
1
1
0 }  
1
2 #删除索引  
3 DELETE /haoke  
4 {  
5
"acknowledged": true  
6 }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
2
.4.2、插入数据  
URL规则:  
POST /{索引}/{类型}/{id}  
1
2
3
4
5
6
7
8
9
POST /haoke/user/1001  
#数据  
{
"id":1001,  
"name":"张三",  
"age":20,  
"sex":""  
}
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
0 #响应  
1 {  
2
ꢀ ꢀ"_index": "haoke",  
3
ꢀ ꢀ"_type": "user",  
ꢀ ꢀ"_id": "1",  
4
5
ꢀ ꢀ"_version": 1,  
ꢀ ꢀ"result": "created",  
ꢀ ꢀ"_shards": {  
6
7
8
ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ },  
9
0
1
2
ꢀ ꢀ"_seq_no": 0,  
ꢀ ꢀ"_primary_term": 1  
3
4 }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
说明:非结构化的索引,不需要事先创建,直接插入数据默认创建索引。  
不指定id插入数据:  
1
2
3
4
5
6
7
POST /haoke/user/  
{
"id":1002,  
"name":"张三",  
"age":20,  
"sex":""  
}
2
.4.3、更新数据  
Elasticsearch中,文档数据是不为修改的,但是可以通过覆盖的方式进行更新。  
1
2
3
4
5
6
7
8
9
PUT /haoke/user/1001  
{
"id":1001,  
"name":"张三",  
"age":21,  
"sex":""  
}
更新结果如下:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
可以看到数据已经被覆盖了。  
问题来了,可以局部更新吗? -- 可以的。  
前面不是说,文档数据不能更新吗? 其实是这样的:  
在内部,依然会查询到这个文档数据,然后进行覆盖操作,步骤如下:  
1
. 从旧文档中检索JSON  
. 修改它  
. 删除旧文档  
. 索引新文档  
2
3
4
示例:  
1
2
3
4
5
6
7
8
9
#注意:这里多了_update标识  
POST /haoke/user/1001/_update  
{
"doc":{  
"age":23  
}
}
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
可以看到数据已经被局部更新了。  
2
.4.4、删除数据  
Elasticsearch中,删除文档数据,只需要发起DELETE请求即可。  
1
DELETE /haoke/user/1001  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
需要注意的是,result表示已经删除,version也更加了。  
如果删除一条不存在的数据,会响应404:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
说明:  
删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的  
时候才会在后台进行删除内容的清理。  
2
.4.5、搜索数据  
根据id搜索数据  
1
2
3
4
5
6
7
8
9
0
1
GET /haoke/user/BbPe_WcB9cFOnF3uebvr  
#返回的数据如下  
{
ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ"_type": "user",  
ꢀ ꢀ"_id": "BbPe_WcB9cFOnF3uebvr",  
ꢀ ꢀ"_version": 8,  
ꢀ ꢀ"found": true,  
ꢀ ꢀ"_source": { ꢀ#原始数据在这里  
ꢀ ꢀ ꢀ ꢀ"id": 1002,  
1
1
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
1
1
1
1
2
ꢀ ꢀ ꢀ ꢀ"name": "李四",  
ꢀ ꢀ ꢀ ꢀ"age": 40,  
ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ }  
3
4
5
6 }  
搜索全部数据  
1
GET /haoke/user/_search  
响应:(默认返回10条数据)  
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  
{
ꢀ ꢀ"took": 26,  
ꢀ ꢀ"timed_out": false,  
ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ"total": 2,  
ꢀ ꢀ ꢀ ꢀ"successful": 2,  
ꢀ ꢀ ꢀ ꢀ"skipped": 0,  
ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ },  
ꢀ ꢀ"hits": {  
ꢀ ꢀ ꢀ ꢀ"total": 4,  
ꢀ ꢀ ꢀ ꢀ"max_score": 1,  
ꢀ ꢀ ꢀ ꢀ"hits": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "BbPe_WcB9cFOnF3uebvr",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"id": 1002,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "李四",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 40,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "1001",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"id": 1001,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "张三",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 20,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
4
4
4
4
4
4
4
4
4
5
5
5
5
5
5
5
5
5
5
6
6
6
6
6
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "1003",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"id": 1003,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "王五",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 30,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ ꢀ }  
2
3
4
5
6
7
8
9
ꢀ ꢀ },  
0
ꢀ ꢀ {  
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "1004",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"id": 1004,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "赵六",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 30,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ ꢀ }  
2
3
4
5
6
7
8
9
0
1
ꢀ ꢀ }  
2
ꢀ ꢀ ]  
3
ꢀ }  
4 }  
关键字搜素数据  
1
#查询年龄等于20的用户  
2
3
GET /haoke/user/_search?q=age:20  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
2
.4.6DSL搜索  
Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。  
DSL(Domain Specific Language特定领域语言)JSON请求体的形式出现。  
1
2
3
4
5
6
7
8
9
POST /haoke/user/_search  
#请求体  
{
ꢀ ꢀ"query" : {  
ꢀ ꢀ ꢀ ꢀ"match" : { ꢀ #match只是查询的一种  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age" : 20  
ꢀ ꢀ }  
ꢀ }  
10 }  
响应数据:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
实现:查询年龄大于30岁的男性用户。  
现有数据:  
1
POST /haoke/user/_search  
2
#请求数据  
3
4
5
6
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"bool": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"filter": {  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
7
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"range": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"gt": 30  
ꢀ ꢀ }  
ꢀ ꢀ }  
8
9
1
1
1
1
1
1
1
1
1
1
2
0
1
2
ꢀ ꢀ },  
3
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"must": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"sex": ""  
ꢀ ꢀ }  
4
5
6
7
ꢀ ꢀ }  
8
ꢀ ꢀ }  
9
ꢀ }  
0 }  
查询结果:  
全文搜索  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
5
6
7
8
9
POST /haoke/user/_search  
#请求数据  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "张三 李四"  
ꢀ ꢀ }  
ꢀ }  
}
POST /haoke/user/_search #请求数据 {  
"
}
query": { "match": { "name": "张三 李四" } }  
2
.4.7、高亮显示  
1
2
3
4
5
6
POST /haoke/user/_search  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "张三 李四"  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
7
ꢀ ꢀ }  
8
ꢀ },  
9
ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": {}  
ꢀ ꢀ }  
1
1
1
1
1
0
1
2
3
ꢀ }  
4 }  
2
.4.8、聚合  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
Elasticsearch中,支持聚合操作,类似SQL中的group by操作。  
1
2
3
4
5
6
7
8
9
0
POST /haoke/user/_search  
{
ꢀ ꢀ"aggs": {  
ꢀ ꢀ ꢀ ꢀ"all_interests": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"terms": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"field": "age"  
ꢀ ꢀ }  
ꢀ ꢀ }  
1
1
ꢀ }  
1 }  
结果:  
从结果可以看出,年龄30的有2条数据,20的有一条,40的一条。  
3、核心详解  
3
.1、文档  
Elasticsearch中,文档以JSON格式进行存储,可以是复杂的结构,如:  
1
2
3
4
5
6
{
ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ"_type": "user",  
ꢀ ꢀ"_id": "1005",  
ꢀ ꢀ"_version": 1,  
ꢀ ꢀ"_score": 1,  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
7
ꢀ ꢀ"_source": {  
8
ꢀ ꢀ ꢀ ꢀ"id": 1005,  
ꢀ ꢀ ꢀ ꢀ"name": "孙七",  
ꢀ ꢀ ꢀ ꢀ"age": 37,  
ꢀ ꢀ ꢀ ꢀ"sex": "",  
ꢀ ꢀ ꢀ ꢀ"card": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"card_number": "123456789"  
ꢀ ꢀ }  
9
1
1
1
1
1
1
1
0
1
2
3
4
5
ꢀ }  
6 }  
其中,card是一个复杂对象,嵌套的Card对象。  
元数据(metadata)  
一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:  
节点  
说明  
_
_
_
index  
文档存储的地方  
文档代表的对象的类  
文档的唯一标识  
type  
id  
_index  
索引(index)类似于关系型数据库里的数据库”——它是我们存储和索引关联数据的地方。  
提示:  
事实上,我们的数据被存储和索引在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空  
间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在索引  
(
index)中。剩下的细节由Elasticsearch关心既可。  
_type  
在应用中,我们使用对象表示一些事物,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一  
(class),这个类定义了属性或与对象关联的数据。 user类的对象可能包含姓名、性别、年龄和Email地址。  
在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为它们有着相同的结构。同理,在Elasticsearch  
中,我们使用相同类型(type)的文档表示相同的事物,因为他们的数据结构也是相同的。  
每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储  
在同一个索引下,但是类型的映射(mapping)会告诉Elasticsearch不同的文档如何被索引。  
_type的名字可以是大写或小写,不能包含下划线或逗号。我们将使用 blog做为类型名。  
_id  
id仅仅是一个字符串,它与 _index_type组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文  
档,你可以自定义 _id,也可以让Elasticsearch帮你自动生成(32位长度)。  
3
.2、查询响应  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
.2.1pretty  
可以在查询url后面添加pretty参数,使得返回的json更易查看。  
3
.2.2、指定响应字段  
在响应的数据中,如果我们不需要全部的字段,可以指定某些需要的字段进行返回。  
1
2
3
4
5
6
7
8
9
0
1
2
GET /haoke/user/1005?_source=id,name  
#响应  
{
ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ"_type": "user",  
ꢀ ꢀ"_id": "1005",  
ꢀ ꢀ"_version": 1,  
ꢀ ꢀ"found": true,  
ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ"name": "孙七",  
ꢀ ꢀ ꢀ ꢀ"id": 1005  
ꢀ }  
1
1
1
1
3 }  
如不需要返回元数据,仅仅返回原始数据,可以这样:  
1
GET /haoke/user/1005/_source  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
还可以这样:  
1
GET /haoke/user/1005/_source?_source=id,name  
3
.3、判断文档是否存在  
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:  
1
HEAD /haoke/user/1005  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
HEAD /haoke/user/1006  
当然,这只表示你在查询的那一刻文档不存在,但并不表示几毫秒后依旧不存在。另一个进程在这期间可能创  
建新文档。  
3
.4、批量操作  
有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。  
3
.4.1、批量查询  
1
2
3
4
5
POST /haoke/user/_mget  
{
}
ꢀ "ids" : [ "1001", "1003" ]  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
如果,某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据。  
1
2
3
4
5
POST /haoke/user/_mget  
{
ꢀ "ids" : [ "1001", "1006" ]  
}
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
.4.2_bulk操作  
Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulkapi完成的。  
请求格式如下:(请求格式不同寻常)  
1
2
3
4
5
{ action: { metadata }}\n  
{ request body ꢀ ꢀ }\n  
{ action: { metadata }}\n  
{ request body ꢀ ꢀ }\n  
...  
批量插入数据:  
1
{"create":{"_index":"haoke","_type":"user","_id":2001}}  
{"id":2001,"name":"name1","age": 20,"sex": ""}  
{"create":{"_index":"haoke","_type":"user","_id":2002}}  
{"id":2002,"name":"name2","age": 20,"sex": ""}  
{"create":{"_index":"haoke","_type":"user","_id":2003}}  
{"id":2003,"name":"name3","age": 20,"sex": ""}  
2
3
4
5
6
7
注意最后一行的回车。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
响应结果:  
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  
{
ꢀ ꢀ"took": 17,  
ꢀ ꢀ"errors": false,  
ꢀ ꢀ"items": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"create": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2001",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "created",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 24,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 201  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"create": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2002",  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
2
2
2
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
5
5
5
5
5
5
5
5
7
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "created",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
8
9
0
1
2
3
4
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 0,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 201  
ꢀ ꢀ }  
5
6
7
8
ꢀ ꢀ },  
9
ꢀ ꢀ {  
0
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"create": {  
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2003",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "created",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
2
3
4
5
6
7
8
9
0
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 201  
ꢀ ꢀ }  
2
3
4
5
ꢀ ꢀ }  
6
ꢀ ]  
7 }  
批量删除:  
1
{"delete":{"_index":"haoke","_type":"user","_id":2001}}  
{"delete":{"_index":"haoke","_type":"user","_id":2002}}  
{"delete":{"_index":"haoke","_type":"user","_id":2003}}  
2
3
4
由于delete没有请求体,所以,action的下一行直接就是下一个action。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
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  
{
ꢀ ꢀ"took": 3,  
ꢀ ꢀ"errors": false,  
ꢀ ꢀ"items": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"delete": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2001",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "deleted",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 25,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 200  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"delete": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2002",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "deleted",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
5
5
5
5
5
5
5
5
2
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
3
4
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 200  
ꢀ ꢀ }  
5
6
7
8
ꢀ ꢀ },  
9
ꢀ ꢀ {  
0
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"delete": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "haoke",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "user",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "2003",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_version": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"result": "deleted",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ ꢀ },  
1
2
3
4
5
6
7
8
9
0
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_seq_no": 3,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_primary_term": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"status": 200  
ꢀ ꢀ }  
2
3
4
5
ꢀ ꢀ }  
6
ꢀ ]  
7 }  
其他操作就类似了。  
一次请求多少性能最高?  
整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一  
个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。  
最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负  
载。  
幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始  
降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以  
使用较小的批次。  
通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的  
批次最好保持在5-15MB大小间。  
3
.5、分页  
SQL使用 LIMIT关键字返回只有一页的结果一样,Elasticsearch接受 fromsize参数:  
1
2
size: 结果数,默认10  
from: 跳过开始的结果数,默认0  
如果你想每页显示5个结果,页码从13,那请求如下:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
GET /_search?size=5  
GET /_search?size=5&from=5  
GET /_search?size=5&from=10  
应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分  
片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。  
1
GET /haoke/user/_search?size=1&from=2  
在集群系统中深度分页  
为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一  
页(结果110)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再  
排序这所有的50个结果以选出顶端的10个结果。  
现在假设我们请求第1000——结果1000110010。工作方式都相同,不同的是每个分片都必须产生顶端的  
1
0010个结果。然后请求节点排序这50050个结果并丢弃50040个!  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
你可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何  
语句不能返回多于1000个结果的原因。  
3
.6、映射  
前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型  
的,否则,自动判断的类型和实际需求是不相符的。  
自动判断的规则如下:  
JSON type  
Field type  
"boolean"  
"long"  
Boolean: true or false  
Whole number: 123  
Floating point: 123.45  
String, valid date: "2014-09-15"  
String: "foo bar"  
"double"  
"date"  
"string"  
Elasticsearch中支持的类型如下:  
类型  
表示的数据类型  
string, text, keyword  
byte, short, integer, long  
float, double  
boolean  
String  
Whole number  
Floating point  
Boolean  
Date  
date  
string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和  
keyword类型替代。  
text 类型,当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型  
以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段  
不用于排序,很少用于聚合。  
keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过  
(比如查找已发布博客中status属性为published的文章)、排序、聚合。keyword类型的字段只能通过精  
确值搜索到。  
创建明确类型的索引:  
1
2
3
4
5
PUT /itcast  
{
ꢀ ꢀ"settings": {  
ꢀ ꢀ ꢀ ꢀ"index": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"number_of_shards": "2",  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
6
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"number_of_replicas": "0"  
ꢀ ꢀ }  
7
8
ꢀ },  
9
ꢀ ꢀ"mappings": {  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
0
ꢀ ꢀ ꢀ ꢀ"person": {  
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"properties": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "text"  
ꢀ ꢀ },  
2
3
4
5
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "integer"  
ꢀ ꢀ },  
6
7
8
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "keyword"  
ꢀ ꢀ },  
9
0
1
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "text"  
ꢀ ꢀ }  
2
3
4
ꢀ ꢀ }  
5
ꢀ ꢀ }  
6
ꢀ }  
7 }  
查看映射:  
1
GET /itcast/_mapping  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
插入数据:  
1
2
3
4
5
6
7
8
9
POST /itcast/_bulk  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}  
{"index":{"_index":"itcast","_type":"person"}}  
10 {"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳"}  
11 {"index":{"_index":"itcast","_type":"person"}}  
12 {"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影"}  
13  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
测试搜索:  
1
2
3
4
5
6
7
8
POST /itcast/person/_search  
{
ꢀ ꢀ"query" : {  
ꢀ ꢀ ꢀ ꢀ"match" : { ꢀ  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby" : "音乐"  
ꢀ ꢀ }  
ꢀ }  
}
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
.7、结构化查询  
3
.7.1term查询  
term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型):  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
ꢀ { "term": { "age": ꢀ ꢀ26 ꢀ ꢀ ꢀ ꢀ }}  
ꢀ { "term": { "date":"2014-09-01" }}  
ꢀ { "term": { "public": true ꢀ ꢀ ꢀ }}  
ꢀ { "term": { "tag": ꢀ ꢀ"full_text" }}  
示例:  
1
2
3
4
5
6
7
8
POST /itcast/person/_search  
{
ꢀ ꢀ"query" : {  
ꢀ ꢀ ꢀ ꢀ"term" : { ꢀ  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age" : 20  
ꢀ ꢀ }  
ꢀ }  
}
3
.7.2terms查询  
terms term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去  
做匹配:  
1
2
3
4
5
{
ꢀ ꢀ"terms": {  
ꢀ ꢀ ꢀ ꢀ"tag": [ "search", "full_text", "nosql" ]  
ꢀ ꢀ }  
}
示例:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
5
6
7
8
POST /itcast/person/_search  
{
ꢀ ꢀ"query" : {  
ꢀ ꢀ ꢀ ꢀ"terms" : { ꢀ  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age" : [20,21]  
ꢀ ꢀ }  
ꢀ }  
}
3
.7.3range查询  
range过滤允许我们按照指定范围查找一批数据:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
5
6
7
8
{
ꢀ ꢀ"range": {  
ꢀ ꢀ ꢀ ꢀ"age": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"gte": ꢀ20,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"lt": ꢀ 30  
ꢀ ꢀ }  
ꢀ }  
}
范围操作符包含:  
gt :: 大于  
gte:: 大于等于  
lt :: 小于  
lte:: 小于等于  
示例:  
1
2
3
4
5
6
7
8
9
0
POST /itcast/person/_search  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"range": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"gte": 20,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"lte": 22  
ꢀ ꢀ }  
ꢀ ꢀ }  
1
1
ꢀ }  
1 }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
.7.4exists 查询  
exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL条件  
1
2
3
4
5
{
ꢀ ꢀ"exists": ꢀ {  
ꢀ ꢀ ꢀ ꢀ"field": ꢀ ꢀ"title"  
ꢀ }  
}
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
这两个查询只是针对已经查出一批数据来,但是想区分出某个字段是否存在的时候使用。  
示例:  
1
2
3
4
5
6
7
8
9
POST /haoke/user/_search  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"exists": { ꢀ#必须包含  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"field": "card"  
ꢀ ꢀ }  
ꢀ }  
}
3
.6.5match查询  
match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。  
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析 match一下查询字符:  
1
2
3
4
5
{
ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ"tweet": "About Search"  
ꢀ }  
}
如果用 match下指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed 的字符串时,它将为你搜索你  
给定的值:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
{ "match": { "age": ꢀ ꢀ26 ꢀ ꢀ ꢀ ꢀ ꢀ }}  
{ "match": { "date": ꢀ "2014-09-01" }}  
{ "match": { "public": true ꢀ ꢀ ꢀ ꢀ }}  
{ "match": { "tag": ꢀ ꢀ"full_text" }}  
3
.7.6bool查询  
bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:  
must :: 多个查询条件的完全匹配,相当于 and。  
must_not :: 多个查询条件的相反匹配,相当于 not。  
should :: 至少有一个查询条件匹配, 相当于 or。  
这些参数可以分别继承一个查询条件或者一个查询条件的数组:  
1
2
3
4
5
6
7
8
9
{
ꢀ ꢀ"bool": {  
ꢀ ꢀ ꢀ ꢀ"must": ꢀ ꢀ { "term": { "folder": "inbox" }},  
ꢀ ꢀ ꢀ ꢀ"must_not": { "term": { "tag": ꢀ ꢀ"spam" }},  
ꢀ ꢀ ꢀ ꢀ"should": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ { "term": { "starred": true }},  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ { "term": { "unread":true }}  
ꢀ ꢀ ]  
ꢀ }  
10 }  
3
.8、过滤查询  
前面讲过结构化查询,Elasticsearch也支持过滤查询,如termrangematch等。  
示例:查询年龄为20岁的用户。  
1
2
3
4
5
6
7
8
9
0
1
POST /itcast/person/_search  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"bool": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"filter": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"term": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 20  
ꢀ ꢀ }  
ꢀ ꢀ }  
1
1
1
ꢀ ꢀ }  
ꢀ }  
2 }  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
查询和过滤的对比  
一条过滤语句会询问每个文档的字段值是否包含着特定值。  
查询语句会询问每个文档的字段值与特定值的匹配程度如何。  
一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且 按照相关性对匹  
配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。  
一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。这些缓存的过滤结果  
集与后续请求的结合使用是非常高效的。  
查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗  
时,并且查询结果也不可缓存。  
建议:  
做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。  
4、中文分词  
4
.1、什么是分词  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
分词就是指将一个文本转化成一系列单词的过程,也叫文本分析,在Elasticsearch中称之为Analysis。  
举例:我是中国人 --> //中国人  
4
.2、分词api  
指定分词器进行分词  
1
2
3
4
5
POST /_analyze  
{
ꢀ ꢀ "analyzer":"standard",  
ꢀ ꢀ "text":"hello world"  
}
结果:  
在结果中不仅可以看出分词的结果,还返回了该词在文本中的位置。  
指定索引分词  
1
2
3
4
5
6
POST /itcast/_analyze  
{
ꢀ ꢀ"analyzer": "standard",  
ꢀ ꢀ"field": "hobby",  
ꢀ ꢀ"text": "听音乐"  
}
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
4
.4、中文分词  
中文分词的难点在于,在汉语中没有明显的词汇分界点,如在英语中,空格可以作为分隔符,如果分隔不正确就会造  
成歧义。  
如:  
//炒肉丝  
///肉丝  
常用中文分词器,IKjiebaTHULAC等,推荐使用IK分词器。  
IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从200612月推出1.0版开始,  
IKAnalyzer已经推出了3个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算  
法的中文分词组件。新版本的IK Analyzer 3.0则发展为面向Java的公用分词组件,独立于Lucene项目,同时提  
供了对Lucene的默认优化实现。  
采用了特有的正向迭代最细粒度切分算法,具有80万字/秒的高速处理能力 采用了多子处理器分析模式,支  
持:英文字母(IP地址、EmailURL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇  
姓名、地名处理)等分词处理。 优化的词典存储,更小的内存占用。  
IK分词器 Elasticsearch插件地址:https://github.com/medcl/elasticsearch-analysis-ik  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
#安装方法:将下载到的elasticsearch-analysis-ik-6.5.4.zip解压到/elasticsearch/plugins/ik目录下  
即可。  
2
3
4
5
6
7
8
9
mkdir es/plugins/ik  
cp elasticsearch-analysis-ik-6.5.4.zip ./es/plugins/ik  
#解压  
unzip elasticsearch-analysis-ik-6.5.4.zip  
#重启  
./bin/elasticsearch  
测试:  
1
2
3
4
5
POST /_analyze  
{
ꢀ ꢀ"analyzer": "ik_max_word",  
ꢀ ꢀ"text": "我是中国人"  
}
结果:  
1
2
3
4
5
6
7
8
9
{
ꢀ ꢀ"tokens": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"token": "",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"start_offset": 0,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"end_offset": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "CN_CHAR",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"position": 0  
ꢀ ꢀ },  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"token": "",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"start_offset": 1,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"end_offset": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "CN_CHAR",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"position": 1  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"token": "中国人",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"start_offset": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"end_offset": 5,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "CN_WORD",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"position": 2  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"token": "中国",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"start_offset": 2,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"end_offset": 4,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "CN_WORD",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"position": 3  
ꢀ ꢀ },  
ꢀ ꢀ {  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
3
3
3
3
3
3
3
2
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"token": "国人",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"start_offset": 3,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"end_offset": 5,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "CN_WORD",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"position": 4  
ꢀ ꢀ }  
3
4
5
6
7
8
ꢀ ]  
9 }  
可以看到,已经对中文进行了分词。  
5、全文搜索  
全文搜索两个最重要的方面是:  
相关性(Relevance) 它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这  
种计算方式可以是 TF/IDF 方法、地理位置邻近、模糊相似,或其他的某些算法。  
分词(Analysis) 它是将文本块转换为有区别的、规范化的 token 的一个过程,目的是为了创建倒排索引以及  
查询倒排索引。  
5
.1、构造数据  
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
PUT /itcast  
{
ꢀ ꢀ"settings": {  
ꢀ ꢀ ꢀ ꢀ"index": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"number_of_shards": "1",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"number_of_replicas": "0"  
ꢀ ꢀ }  
ꢀ },  
ꢀ ꢀ"mappings": {  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
ꢀ ꢀ ꢀ ꢀ"person": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"properties": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "text"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "integer"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "keyword"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"type": "text",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"analyzer":"ik_max_word"  
ꢀ ꢀ }  
ꢀ ꢀ }  
ꢀ ꢀ }  
ꢀ }  
8 }  
批量插入数据:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
2
3
4
5
6
7
8
9
POST http://172.16.55.185:9200/itcast/_bulk  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}  
{"index":{"_index":"itcast","_type":"person"}}  
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}  
{"index":{"_index":"itcast","_type":"person"}}  
10 {"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳、篮球"}  
11 {"index":{"_index":"itcast","_type":"person"}}  
12 {"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影、羽毛球"}  
13  
结果:  
5
.2、单词搜索  
1
2
3
4
5
6
7
8
9
0
1
2
3
POST /itcast/person/_search  
{
"query":{  
"match":{  
"hobby":"音乐"  
}
},  
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
1
1
1
1
1
ꢀ }  
4 }  
结果:  
1
2
3
4
5
6
7
8
9
{
ꢀ ꢀ"took": 9,  
ꢀ ꢀ"timed_out": false,  
ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ"skipped": 0,  
ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ },  
10  
ꢀ ꢀ"hits": {  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
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 }  
ꢀ ꢀ ꢀ ꢀ"total": 2,  
ꢀ ꢀ ꢀ ꢀ"max_score": 0.6841192,  
ꢀ ꢀ ꢀ ꢀ"hits": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "Uv0cDWgBR-bSw8-LpdkZ",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 0.6841192,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "王五",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 22,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "333@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "羽毛球、篮球、游泳、听音乐"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"羽毛球、篮球、游泳、听<em>音乐</em>"  
ꢀ ꢀ ]  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "VP0cDWgBR-bSw8-LpdkZ",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 0.6841192,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "孙七",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 24,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "555@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "听音乐、看电影、羽毛球"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"<em>音乐</em>、看电影、羽毛球"  
ꢀ ꢀ ]  
ꢀ ꢀ }  
ꢀ ꢀ }  
ꢀ ꢀ ]  
ꢀ }  
过程说明:  
. 检查字段类型  
1
爱好 hobby 字段是一个 text 类型( 指定了IK分词器),这意味着查询字符串本身也应该被分词。  
. 分析查询字符串 。  
2
将查询的字符串 音乐传入IK分词器中,输出的结果是单个项 音乐。因为只有一个单词项,所以 match 查询执  
行的是单个底层 term 查询。  
3. 查找匹配文档 。  
term 查询在倒排索引中查找 音乐然后获取一组包含该项的文档,本例的结果是文档:3 5 。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
4. 为每个文档评分 。  
term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 音乐在相关文档的  
hobby 字段中出现的频率)和 反向文档频率(inverse document frequency,即词 音乐在所有文档的  
hobby 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。  
5
.3、多词搜索  
1
2
3
4
5
6
7
8
9
0
1
2
POST /itcast/person/_search  
{
"query":{  
"match":{  
"hobby":"音乐 篮球"  
}
},  
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
1
1
1
1
ꢀ }  
3 }  
结果:  
1
2
3
4
5
6
7
8
9
{
ꢀ ꢀ"took": 3,  
ꢀ ꢀ"timed_out": false,  
ꢀ ꢀ"_shards": {  
ꢀ ꢀ ꢀ ꢀ"total": 1,  
ꢀ ꢀ ꢀ ꢀ"successful": 1,  
ꢀ ꢀ ꢀ ꢀ"skipped": 0,  
ꢀ ꢀ ꢀ ꢀ"failed": 0  
ꢀ },  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
ꢀ ꢀ"hits": {  
ꢀ ꢀ ꢀ ꢀ"total": 4,  
ꢀ ꢀ ꢀ ꢀ"max_score": 1.3192271,  
ꢀ ꢀ ꢀ ꢀ"hits": [  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "Uv0cDWgBR-bSw8-LpdkZ",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 1.3192271,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "王五",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 22,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "333@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "羽毛球、篮球、游泳、听音乐"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"羽毛球、<em>篮球</em>、游泳、听<em>音乐</em>"  
ꢀ ꢀ ]  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
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  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "VP0cDWgBR-bSw8-LpdkZ",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 0.81652206,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "孙七",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 24,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "555@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "听音乐、看电影、羽毛球"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"<em>音乐</em>、看电影、羽毛球"  
ꢀ ꢀ ]  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "Vf0gDWgBR-bSw8-LOdm_",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 0.6987338,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "赵六",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 23,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "444@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "跑步、游泳、篮球"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"跑步、游泳、<em>篮球</em>"  
ꢀ ꢀ ]  
ꢀ ꢀ }  
ꢀ ꢀ },  
ꢀ ꢀ {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_index": "itcast",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_type": "person",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_id": "Uf0cDWgBR-bSw8-LpdkZ",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_score": 0.50270504,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"_source": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"name": "李四",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"age": 21,  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"mail": "222@qq.com",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": "羽毛球、乒乓球、足球、篮球"  
ꢀ ꢀ },  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"highlight": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": [  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"羽毛球、乒乓球、足球、<em>篮球</em>"  
ꢀ ꢀ ]  
ꢀ ꢀ }  
ꢀ ꢀ }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
8
8
8
2
ꢀ ꢀ ]  
ꢀ }  
3
4 }  
可以看到,包含了音乐篮球的数据都已经被搜索到了。  
可是,搜索的结果并不符合我们的预期,因为我们想搜索的是既包含音乐又包含篮球的用户,显然结果返回  
的关系。  
Elasticsearch中,可以指定词之间的逻辑关系,如下:  
1
POST /itcast/person/_search  
2
{
3
"query":{  
"match":{  
"hobby":{  
4
5
6
"query":"音乐 篮球",  
"operator":"and"  
7
8
}
9
}
1
1
1
1
1
1
1
0
},  
1
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
2
3
4
5
ꢀ }  
6 }  
结果:  
可以看到结果符合预期。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
前面我们测试了“OR” “AND”搜索,这是两个极端,其实在实际场景中,并不会选取这2个极端,更有可能是选取这  
种,或者说,只需要符合一定的相似度就可以查询到数据,在Elasticsearch中也支持这样的查询,通过  
minimum_should_match来指定匹配度,如:70%;  
示例:  
1
2
3
4
5
6
7
8
9
0
1
2
3
4
{
"query":{  
"match":{  
"hobby":{  
"query":"游泳 羽毛球",  
"minimum_should_match":"80%"  
}
}
},  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
ꢀ }  
5 }  
6
7 #结果:省略显示  
8 "hits": {  
9
0
1
2
3
4
ꢀ ꢀ ꢀ ꢀ"total": 4, #相似度为80%的情况下,查询到4条数据  
ꢀ ꢀ ꢀ ꢀ"max_score": 1.621458,  
ꢀ ꢀ ꢀ ꢀ"hits": [  
ꢀ ꢀ .........  
ꢀ ꢀ }  
5 #设置40%进行测试:  
6 {  
7
"query":{  
8
"match":{  
"hobby":{  
"query":"游泳 羽毛球",  
"minimum_should_match":"40%"  
9
0
1
2
}
3
}
4
},  
5
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
6
7
8
9
ꢀ }  
0 }  
1 #结果:  
2
3
4
5
6
7
"hits": {  
ꢀ ꢀ ꢀ ꢀ"total": 5, ꢀ#相似度为40%的情况下,查询到5条数据  
ꢀ ꢀ ꢀ ꢀ"max_score": 1.621458,  
ꢀ ꢀ ꢀ ꢀ"hits": [  
ꢀ ꢀ ........  
ꢀ }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
相似度应该多少合适,需要在实际的需求中进行反复测试,才可得到合理的值。  
5
.4、组合搜索  
在搜索时,也可以使用过滤器中讲过的bool组合查询,示例:  
1
POST /itcast/person/_search  
2
3
{
4
"query":{  
"bool":{  
"must":{  
"match":{  
5
6
7
8
"hobby":"篮球"  
9
}
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
0
},  
1
"must_not":{  
"match":{  
2
3
"hobby":"音乐"  
4
}
5
},  
6
"should":[  
{
7
8
"match": {  
"hobby":"游泳"  
9
0
}
1
}
2
]
3
}
4
},  
5
"highlight": {  
6
ꢀ ꢀ ꢀ ꢀ"fields": {  
7
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
8
ꢀ ꢀ }  
ꢀ }  
9
0 }  
上面搜索的意思是:  
搜索结果中必须包含篮球,不能包含音乐,如果包含了游泳,那么它的相似度更高。  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
评分的计算规则  
bool 查询会为每个文档计算相关度评分 _score , 再将所有匹配的 must should 语句的分数 _score 求和,  
最后除以 must should 语句的总数。  
must_not 语句不会影响评分; 它的作用只是将不相关的文档排除。  
默认情况下,should中的内容不是必须匹配的,如果查询语句中没有must,那么就会至少匹配其中一个。当然了,  
也可以通过minimum_should_match参数进行控制,该值可以是数字也可以的百分比。  
示例:  
1
2
3
4
5
6
POST /itcast/person/_search  
{
"query":{  
"bool":{  
"should":[  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
7
{
8
"match": {  
"hobby":"游泳"  
9
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
0
}
1
},  
{
2
3
"match": {  
4
"hobby":"篮球"  
5
}
6
},  
{
7
8
"match": {  
9
"hobby":"音乐"  
0
}
1
}
2
],  
"minimum_should_match":2  
3
4
}
5
},  
6
"highlight": {  
ꢀ ꢀ ꢀ ꢀ"fields": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
7
8
9
0
ꢀ }  
1 }  
minimum_should_match2,意思是should中的三个词,至少要满足2个。  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
5
.5、权重  
有些时候,我们可能需要对某些词增加权重来影响该条数据的得分。如下:  
搜索关键字为游泳篮球,如果结果中包含了音乐权重为10,包含了跑步权重为2。  
1
2
3
4
5
6
7
8
9
0
1
2
POST /itcast/person/_search  
{
ꢀ ꢀ"query": {  
ꢀ ꢀ ꢀ ꢀ"bool": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"must": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"query": "游泳篮球",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"operator": "and"  
ꢀ ꢀ }  
ꢀ ꢀ }  
1
1
1
ꢀ ꢀ },  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"should": [  
ꢀ ꢀ {  
4
5
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"query": "音乐",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"boost": 10  
ꢀ ꢀ }  
ꢀ ꢀ }  
6
7
8
9
0
1
ꢀ ꢀ },  
2
ꢀ ꢀ {  
3
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"match": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"query": "跑步",  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"boost": 2  
ꢀ ꢀ }  
ꢀ ꢀ }  
4
5
6
7
8
9
ꢀ ꢀ }  
0
ꢀ ꢀ ]  
1
ꢀ ꢀ }  
2
ꢀ },  
3
ꢀ ꢀ"highlight": {  
4
ꢀ ꢀ ꢀ ꢀ"fields": {  
5
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ"hobby": {}  
ꢀ ꢀ }  
6
7
ꢀ }  
8 }  
结果:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
如果不设置权重的查询结果是这样:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
6Elasticsearch集群  
6
.1、集群节点  
ELasticsearch的集群是由多个节点组成的,通过cluster.name设置集群名称,并且用于区分其它的集群,每个节点  
通过node.name指定节点的名称。  
Elasticsearch中,节点的类型主要有4种:  
master节点  
配置文件中node.master属性为true(默认为true),就有资格被选为master节点。  
master节点用于控制整个集群的操作。比如创建或删除索引,管理其它非master节点等。  
data节点  
配置文件中node.data属性为true(默认为true),就有资格被设置成data节点。  
data节点主要用于执行数据相关的操作。比如文档的CRUD。  
客户端节点  
配置文件中node.master属性和node.data属性均为false。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
该节点不能作为master节点,也不能作为data节点。  
可以作为客户端节点,用于响应用户的请求,把请求转发到其他节点  
部落节点  
当一个节点配置tribe.*的时候,它是一个特殊的客户端,它可以连接多个集群,在所有连接的集群上执行  
搜索和其他操作。  
6
.2、搭建集群  
1
2
3
4
5
6
7
8
9
#启动3个虚拟机,分别在3台虚拟机上部署安装Elasticsearch  
mkdir /itcast/es-cluster  
#分发到其它机器  
scp -r es-cluster elsearch@192.168.40.134:/itcast  
#node01的配置:  
cluster.name: es-itcast-cluster  
node.name: node01  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
0 node.master: true  
1 node.data: true  
2 network.host: 0.0.0.0  
3 http.port: 9200  
4 discovery.zen.ping.unicast.hosts: ["192.168.40.133","192.168.40.134","192.168.40.135"]  
5 discovery.zen.minimum_master_nodes: 2  
6 http.cors.enabled: true  
7 http.cors.allow-origin: "*"  
8
9 #node02的配置:  
0 cluster.name: es-itcast-cluster  
1 node.name: node02  
2 node.master: true  
3 node.data: true  
4 network.host: 0.0.0.0  
5 http.port: 9200  
6 discovery.zen.ping.unicast.hosts: ["192.168.40.133","192.168.40.134","192.168.40.135"]  
7 discovery.zen.minimum_master_nodes: 2  
8 http.cors.enabled: true  
9 http.cors.allow-origin: "*"  
0
1 #node03的配置:  
2 cluster.name: es-itcast-cluster  
3 node.name: node02  
4 node.master: true  
5 node.data: true  
6 network.host: 0.0.0.0  
7 http.port: 9200  
8 discovery.zen.ping.unicast.hosts: ["192.168.40.133","192.168.40.134","192.168.40.135"]  
9 discovery.zen.minimum_master_nodes: 2  
0 http.cors.enabled: true  
1 http.cors.allow-origin: "*"  
2
3 #分别启动3个节点  
4 ./elasticsearch  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
45  
查看集群:  
创建索引:  
查询集群状态:/_cluster/health  
响应:  
1
{
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
2
ꢀ ꢀcluster_name: "es-itcast-cluster"  
ꢀ ꢀstatus: "green"  
3
4
ꢀ ꢀtimed_out: false  
5
ꢀ ꢀnumber_of_nodes: 3  
6
ꢀ ꢀnumber_of_data_nodes: 3  
ꢀ ꢀactive_primary_shards: 5  
ꢀ ꢀactive_shards: 10  
7
8
9
ꢀ ꢀrelocating_shards: 0  
1
1
1
1
1
1
1
1
0
ꢀ ꢀinitializing_shards: 0  
ꢀ ꢀunassigned_shards: 0  
1
2
ꢀ ꢀdelayed_unassigned_shards: 0  
ꢀ ꢀnumber_of_pending_tasks: 0  
ꢀ ꢀnumber_of_in_flight_fetch: 0  
ꢀ ꢀtask_max_waiting_in_queue_millis: 0  
ꢀ ꢀactive_shards_percent_as_number: 100  
3
4
5
6
7 }  
集群状态的三种颜色:  
颜色  
意义  
green  
yellow  
red  
所有主要分片和复制分片都可用  
所有主要分片可用,但不是所有复制分片都可用  
不是所有的主要分片都可用  
6
.3、分片和副本  
为了将数据添加到Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来  
指向一个或多个分片(shards)逻辑命名空间(logical namespace)”.  
一个分片(shard)是一个最小级别工作单元(worker unit)”,它只是保存了索引中所有数据的一部分。  
我们需要知道是分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。应用程序不会和它直接通  
信。  
分片可以是主分片(primary shard)或者是复制分片(replica shard)。  
索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。  
复制分片只是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者  
从别的shard取回文档。  
当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整。  
6
.4、故障转移  
6
.4.1、将data节点停止  
这里选择将node02停止:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
说明:  
当前集群状态为黄色,表示主节点可用,副本节点不完全可用  
过一段时间观察,发现节点列表中看不到node02,副本节点分配到了node01node03,集群状态恢复到绿色。  
node02恢复:  
1
./node02/bin/elasticsearch  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
可以看到,node02恢复后,重新加入了集群,并且重新分配了节点信息。  
6
.4.2、将master节点停止  
接下来,测试将node01停止,也就是将主节点停止。  
从结果中可以看出,集群对master进行了重新选举,选择node03master。并且集群状态变成黄色。  
等待一段时间后,集群状态从黄色变为了绿色:  
恢复node01节点:  
1
./node01/bin/elasticsearch  
重启之后,发现node01可以正常加入到集群中,集群状态依然为绿色:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
特别说明:  
如果在配置文件中discovery.zen.minimum_master_nodes设置的不是N/2+1时,会出现脑裂问题,之前宕机  
的主节点恢复后不会加入到集群。  
6
.5、分布式文档  
6
.5.1、路由  
首先,来看个问题:  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
如图所示:当我们想一个集群保存文档时,文档该存储到哪个节点呢? 是随机吗? 是轮询吗?  
实际上,在ELasticsearch中,会采用计算的方式来确定存储到哪个节点,计算公式如下:  
1
shard = hash(routing) % number_of_primary_shards  
routing值是一个任意字符串,它默认是_id但也可以自定义。  
这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数  
的范围永远是0number_of_primary_shards - 1,这个数字就是特定文档所在的分片。  
这就是为什么创建了主分片后,不能修改的原因。  
6
.5.2、文档的写操作  
新建、索引和删除请求都是(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。  
下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:  
1
. 客户端给 Node 1发送新建、索引或删除请求。  
. 节点使用文档的 _id确定文档属于分片 0。它转发请求到 Node 3,分片 0位于这个节点上。  
. Node 3在主分片上执行请求,如果成功,它转发请求到相应的位于 Node 1 Node 2的复制节点上。当所有  
的复制节点报告成功, Node 3报告成功到请求的节点,请求的节点再报告给客户端。  
2
3
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了。  
6
.5.3、搜索文档(单个文档)  
文档能够从主分片或任意一个复制分片被检索。  
下面我们罗列在主分片或复制分片上检索一个文档必要的顺序步骤:  
1
. 客户端给 Node 1发送get请求。  
. 节点使用文档的 _id确定文档属于分片 0。分片 0对应的复制分片在三个节点上都有。此时,它转发请求到  
Node 2。  
2
3. Node 2返回文档(document) Node 1然后返回给客户端。  
对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——它会循环所有分片副本。  
可能的情况是,一个被索引的文档已经存在于主分片上却还没来得及同步到复制分片上。这时复制分片会报告文档未  
找到,主分片会成功返回文档。一旦索引请求成功返回给用户,文档则在主分片和复制分片都是可用的。  
6
.5.4、全文搜索  
对于全文搜索而言,文档可能分散在各个节点上,那么在分布式的情况下,如何搜索文档呢?  
搜索,分为2个阶段,搜索(query+取回(fetch)。  
搜索(query)  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
查询阶段包含以下三步:  
1
. 客户端发送一个 search(搜索) 请求给 Node 3, Node 3创建了一个长度为 from+size的空优先级队  
. Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到  
一个大小为 from+size的有序本地优先队列里去。  
2
3. 每个分片返回documentID和它优先队列里的所有document的排序值给协调节点 Node 3Node 3把这些  
值合并到自己的优先队列里产生全局排序结果。  
取回(fetch)  
分发阶段由以下步骤构成:  
1
. 协调节点辨别出哪个document需要取回,并且向相关分片发出 GET请求。  
. 每个分片加载document并且根据需要 enrich 它们,然后再将document返回协调节点。  
. 一旦所有的document都被取回,协调节点会将结果返回给客户端。  
2
3
7Java客户端  
Elasticsearch中,为java提供了2种客户端,一种是REST风格的客户端,另一种是Java API的客户端。  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
7
.1REST客户端  
Elasticsearch提供了2REST客户端,一种是低级客户端,一种是高级客户端。  
Java Low Level REST Client:官方提供的低级客户端。该客户端通过http来连接Elasticsearch集群。用户在使  
用该客户端时需要将请求数据手动拼接成Elasticsearch所需JSON格式进行发送,收到响应时同样也需要将返回  
JSON数据手动封装成对象。虽然麻烦,不过该客户端兼容所有的Elasticsearch版本。  
Java High Level REST Client:官方提供的高级客户端。该客户端基于低级客户端实现,它提供了很多便捷的  
API来解决低级客户端需要手动转换数据格式的问题。  
7
.2、构造数据  
1
2
3
4
5
6
7
8
9
POST /haoke/house/_bulk  
{"index":{"_index":"haoke","_type":"house"}}  
{"id":"1001","title":"整租 · 南丹大楼 1居室 7500","price":"7500"}  
{"index":{"_index":"haoke","_type":"house"}}  
{"id":"1002","title":"陆家嘴板块,精装设计一室一厅,可拎包入住诚意租。","price":"8500"}  
{"index":{"_index":"haoke","_type":"house"}}  
{"id":"1003","title":"整租 · 健安坊 1居室 4050","price":"7500"}  
{"index":{"_index":"haoke","_type":"house"}}  
1
1
1
1
1
0 {"id":"1004","title":"整租 · 中凯城市之光+视野开阔+景色秀丽+拎包入住","price":"6500"}  
1 {"index":{"_index":"haoke","_type":"house"}}  
2 {"id":"1005","title":"整租 · 南京西路品质小区 21213三轨交汇 配套齐* 拎包入住","price":"6000"}  
3 {"index":{"_index":"haoke","_type":"house"}}  
4 {"id":"1006","title":"祥康里 简约风格 *南户型 拎包入住 看房随时","price":"7000"}  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
15  
7
.3REST低级客户端  
7
.3.1、创建工程  
创建工程itcast-elasticsearch:  
1
2
3
4
<?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">  
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  
ꢀ ꢀ<modelVersion>4.0.0</modelVersion>  
ꢀ ꢀ<groupId>cn.itcast.elasticsearch</groupId>  
ꢀ ꢀ<artifactId>itcast-elasticsearch</artifactId>  
ꢀ ꢀ<version>1.0-SNAPSHOT</version>  
ꢀ ꢀ<dependencies>  
ꢀ ꢀ ꢀ ꢀ<dependency>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<groupId>org.elasticsearch.client</groupId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<artifactId>elasticsearch-rest-client</artifactId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<version>6.5.4</version>  
ꢀ ꢀ ꢀ ꢀ</dependency>  
ꢀ ꢀ ꢀ ꢀ<dependency>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<groupId>junit</groupId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<artifactId>junit</artifactId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<version>4.12</version>  
ꢀ ꢀ ꢀ ꢀ</dependency>  
ꢀ ꢀ ꢀ ꢀ<dependency>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<groupId>com.fasterxml.jackson.core</groupId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<artifactId>jackson-databind</artifactId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<version>2.9.4</version>  
ꢀ ꢀ ꢀ ꢀ</dependency>  
ꢀ ꢀ</dependencies>  
ꢀ ꢀ<build>  
ꢀ ꢀ ꢀ ꢀ<plugins>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<!-- java编译插件 -->  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<plugin>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<groupId>org.apache.maven.plugins</groupId>  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
3
3
3
3
4
4
4
4
4
4
5
6
7
8
9
0
1
2
3
4
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<artifactId>maven-compiler-plugin</artifactId>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<version>3.2</version>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<configuration>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<source>1.8</source>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<target>1.8</target>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ<encoding>UTF-8</encoding>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ</configuration>  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ</plugin>  
ꢀ ꢀ ꢀ ꢀ</plugins>  
ꢀ ꢀ</build>  
5 </project>  
7
.3.2、编写测试用例  
1
2
3
4
5
6
7
8
9
0
package cn.itcast.es.rest;  
import com.fasterxml.jackson.databind.ObjectMapper;  
import org.apache.http.HttpHost;  
import org.apache.http.util.EntityUtils;  
import org.elasticsearch.client.*;  
import org.junit.After;  
import org.junit.Before;  
import org.junit.Test;  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
1 import java.io.IOException;  
2 import java.util.HashMap;  
3 import java.util.Map;  
4
5 public class TestESREST {  
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
ꢀ ꢀprivate static final ObjectMapper MAPPER = new ObjectMapper();  
ꢀ ꢀprivate RestClient restClient;  
ꢀ ꢀ@Before  
ꢀ ꢀpublic void init() {  
ꢀ ꢀ ꢀ ꢀRestClientBuilder restClientBuilder = RestClient.builder(  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9200, "http"),  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9201, "http"),  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9202, "http"));  
ꢀ ꢀ ꢀ ꢀrestClientBuilder.setFailureListener(new RestClient.FailureListener() {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ@Override  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀpublic void onFailure(Node node) {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("出错了 -> " + node);  
ꢀ ꢀ }  
ꢀ ꢀ });  
ꢀ ꢀ ꢀ ꢀthis.restClient = restClientBuilder.build();  
ꢀ }  
ꢀ ꢀ@After  
ꢀ ꢀpublic void after() throws IOException {  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
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  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
ꢀ ꢀ ꢀ ꢀrestClient.close();  
ꢀ }  
ꢀ ꢀ// 查询集群状态  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testGetInfo() throws IOException {  
ꢀ ꢀ ꢀ ꢀRequest request = new Request("GET", "/_cluster/state");  
ꢀ ꢀ ꢀ ꢀrequest.addParameter("pretty","true");  
ꢀ ꢀ ꢀ ꢀResponse response = this.restClient.performRequest(request);  
ꢀ ꢀ ꢀ ꢀSystem.out.println(response.getStatusLine());  
ꢀ ꢀ ꢀ ꢀSystem.out.println(EntityUtils.toString(response.getEntity()));  
ꢀ }  
ꢀ ꢀ// 新增数据  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testCreateData() throws IOException {  
ꢀ ꢀ ꢀ ꢀRequest request = new Request("POST", "/haoke/house");  
ꢀ ꢀ ꢀ ꢀMap<String, Object> data = new HashMap<>();  
ꢀ ꢀ ꢀ ꢀdata.put("id","2001");  
ꢀ ꢀ ꢀ ꢀdata.put("title","张江高科");  
ꢀ ꢀ ꢀ ꢀdata.put("price","3500");  
ꢀ ꢀ ꢀ ꢀrequest.setJsonEntity(MAPPER.writeValueAsString(data));  
ꢀ ꢀ ꢀ ꢀResponse response = this.restClient.performRequest(request);  
ꢀ ꢀ ꢀ ꢀSystem.out.println(response.getStatusLine());  
ꢀ ꢀ ꢀ ꢀSystem.out.println(EntityUtils.toString(response.getEntity()));  
ꢀ }  
ꢀ ꢀ// 根据id查询数据  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testQueryData() throws IOException {  
ꢀ ꢀ ꢀ ꢀRequest request = new Request("GET", "/haoke/house/G0pfE2gBCKv8opxuRz1y");  
ꢀ ꢀ ꢀ ꢀResponse response = this.restClient.performRequest(request);  
ꢀ ꢀ ꢀ ꢀSystem.out.println(response.getStatusLine());  
ꢀ ꢀ ꢀ ꢀSystem.out.println(EntityUtils.toString(response.getEntity()));  
ꢀ }  
ꢀ ꢀ// 搜索数据  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testSearchData() throws IOException {  
ꢀ ꢀ ꢀ ꢀRequest request = new Request("POST", "/haoke/house/_search");  
ꢀ ꢀ ꢀ ꢀString searchJson = "{\"query\": {\"match\": {\"title\": \"拎包入住\"}}}";  
ꢀ ꢀ ꢀ ꢀrequest.setJsonEntity(searchJson);  
ꢀ ꢀ ꢀ ꢀrequest.addParameter("pretty","true");  
ꢀ ꢀ ꢀ ꢀResponse response = this.restClient.performRequest(request);  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
9
9
9
9
9
9
9
3
ꢀ ꢀ ꢀ ꢀSystem.out.println(response.getStatusLine());  
4
ꢀ ꢀ ꢀ ꢀSystem.out.println(EntityUtils.toString(response.getEntity()));  
ꢀ }  
5
6
7
8 }  
9
从使用中,可以看出,基本和我们使用RESTful api使用几乎是一致的。  
7
.4REST高级客户端  
7
.4.1、引入依赖  
1
2
3
4
5
<dependency>  
ꢀ ꢀ<groupId>org.elasticsearch.client</groupId>  
ꢀ ꢀ<artifactId>elasticsearch-rest-high-level-client</artifactId>  
ꢀ ꢀ<version>6.5.4</version>  
</dependency>  
7
.4.2、编写测试用例  
1
2
3
4
5
6
7
8
9
package cn.itcast.es.rest;  
import org.apache.http.HttpHost;  
import org.elasticsearch.action.ActionListener;  
import org.elasticsearch.action.delete.DeleteRequest;  
import org.elasticsearch.action.delete.DeleteResponse;  
import org.elasticsearch.action.get.GetRequest;  
import org.elasticsearch.action.get.GetResponse;  
import org.elasticsearch.action.index.IndexRequest;  
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
3
0 import org.elasticsearch.action.index.IndexResponse;  
1 import org.elasticsearch.action.search.SearchRequest;  
2 import org.elasticsearch.action.search.SearchResponse;  
3 import org.elasticsearch.action.update.UpdateRequest;  
4 import org.elasticsearch.action.update.UpdateResponse;  
5 import org.elasticsearch.client.RequestOptions;  
6 import org.elasticsearch.client.RestClient;  
7 import org.elasticsearch.client.RestClientBuilder;  
8 import org.elasticsearch.client.RestHighLevelClient;  
9 import org.elasticsearch.common.Strings;  
0 import org.elasticsearch.common.unit.TimeValue;  
1 import org.elasticsearch.index.query.QueryBuilders;  
2 import org.elasticsearch.search.SearchHit;  
3 import org.elasticsearch.search.SearchHits;  
4 import org.elasticsearch.search.builder.SearchSourceBuilder;  
5 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;  
6 import org.junit.After;  
7 import org.junit.Before;  
8 import org.junit.Test;  
9
0 import java.util.HashMap;  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
5
5
5
5
5
5
5
5
5
5
6
6
6
6
6
6
6
6
6
6
1 import java.util.Map;  
2 import java.util.concurrent.TimeUnit;  
3
4 public class TestRestHighLevel {  
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
ꢀ ꢀprivate RestHighLevelClient client;  
ꢀ ꢀ@Before  
ꢀ ꢀpublic void init() {  
ꢀ ꢀ ꢀ ꢀRestClientBuilder restClientBuilder = RestClient.builder(  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9200, "http"),  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9201, "http"),  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew HttpHost("172.16.55.185", 9202, "http"));  
ꢀ ꢀ ꢀ ꢀthis.client = new RestHighLevelClient(restClientBuilder);  
ꢀ }  
ꢀ ꢀ@After  
ꢀ ꢀpublic void after() throws Exception {  
ꢀ ꢀ ꢀ ꢀthis.client.close();  
ꢀ }  
ꢀ ꢀ/**  
ꢀ ꢀ * 新增文档,同步操作  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testCreate() throws Exception {  
ꢀ ꢀ ꢀ ꢀMap<String, Object> data = new HashMap<>();  
ꢀ ꢀ ꢀ ꢀdata.put("id", "2002");  
ꢀ ꢀ ꢀ ꢀdata.put("title", "南京西路 拎包入住 一室一厅");  
ꢀ ꢀ ꢀ ꢀdata.put("price", "4500");  
ꢀ ꢀ ꢀ ꢀIndexRequest indexRequest = new IndexRequest("haoke", "house")  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ .source(data);  
ꢀ ꢀ ꢀ ꢀIndexResponse indexResponse = this.client.index(indexRequest,  
RequestOptions.DEFAULT);  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
ꢀ ꢀ ꢀ ꢀSystem.out.println("id->" + indexResponse.getId());  
ꢀ ꢀ ꢀ ꢀSystem.out.println("index->" + indexResponse.getIndex());  
ꢀ ꢀ ꢀ ꢀSystem.out.println("type->" + indexResponse.getType());  
ꢀ ꢀ ꢀ ꢀSystem.out.println("version->" + indexResponse.getVersion());  
ꢀ ꢀ ꢀ ꢀSystem.out.println("result->" + indexResponse.getResult());  
ꢀ ꢀ ꢀ ꢀSystem.out.println("shardInfo->" + indexResponse.getShardInfo());  
ꢀ }  
ꢀ ꢀ/**  
ꢀ ꢀ * 新增文档,异步操作  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testCreateAsync() throws Exception {  
ꢀ ꢀ ꢀ ꢀMap<String, Object> data = new HashMap<>();  
ꢀ ꢀ ꢀ ꢀdata.put("id", "2003");  
ꢀ ꢀ ꢀ ꢀdata.put("title", "南京东路 最新房源 二室一厅");  
ꢀ ꢀ ꢀ ꢀdata.put("price", "5500");  
ꢀ ꢀ ꢀ ꢀIndexRequest indexRequest = new IndexRequest("haoke", "house")  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ .source(data);  
ꢀ ꢀ ꢀ ꢀthis.client.indexAsync(indexRequest, RequestOptions.DEFAULT, new  
ActionListener<IndexResponse>() {  
9
9
9
9
6
7
8
9
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ@Override  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀpublic void onResponse(IndexResponse indexResponse) {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("id->" + indexResponse.getId());  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("index->" + indexResponse.getIndex());  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("type->" + indexResponse.getType());  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("version->" + indexResponse.getVersion());  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("result->" + indexResponse.getResult());  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println("shardInfo->" + indexResponse.getShardInfo());  
ꢀ ꢀ }  
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
00  
01  
02  
03  
04  
05  
06  
07  
08  
09  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ@Override  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀpublic void onFailure(Exception e) {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println(e);  
ꢀ ꢀ }  
ꢀ ꢀ });  
ꢀ ꢀ ꢀ ꢀ  
ꢀ ꢀ ꢀ ꢀSystem.out.println("ok");  
ꢀ ꢀ ꢀ ꢀThread.sleep(20000);  
ꢀ }  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testQuery() throws Exception {  
ꢀ ꢀ ꢀ ꢀGetRequest getRequest = new GetRequest("haoke", "house",  
"GkpdE2gBCKv8opxuOj12");  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
ꢀ ꢀ ꢀ ꢀ// 指定返回的字段  
ꢀ ꢀ ꢀ ꢀString[] includes = new String[]{"title", "id"};  
ꢀ ꢀ ꢀ ꢀString[] excludes = Strings.EMPTY_ARRAY;  
ꢀ ꢀ ꢀ ꢀFetchSourceContext fetchSourceContext =  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀ ꢀnew FetchSourceContext(true, includes, excludes);  
ꢀ ꢀ ꢀ ꢀgetRequest.fetchSourceContext(fetchSourceContext);  
ꢀ ꢀ ꢀ ꢀGetResponse response = this.client.get(getRequest, RequestOptions.DEFAULT);  
ꢀ ꢀ ꢀ ꢀSystem.out.println("数据 -> " + response.getSource());  
ꢀ }  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
134  
135  
136  
137  
138  
139  
140  
141  
142  
ꢀ ꢀ/**  
ꢀ ꢀ * 判断是否存在  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testExists() throws Exception {  
ꢀ ꢀ ꢀ ꢀGetRequest getRequest = new GetRequest("haoke", "house",  
GkpdE2gBCKv8opxuOj12");  
"
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
ꢀ ꢀ ꢀ ꢀ// 不返回的字段  
ꢀ ꢀ ꢀ ꢀgetRequest.fetchSourceContext(new FetchSourceContext(false));  
ꢀ ꢀ ꢀ ꢀboolean exists = this.client.exists(getRequest, RequestOptions.DEFAULT);  
ꢀ ꢀ ꢀ ꢀSystem.out.println("exists -> " + exists);  
ꢀ }  
ꢀ ꢀ/**  
ꢀ ꢀ * 删除数据  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testDelete() throws Exception {  
ꢀ ꢀ ꢀ ꢀDeleteRequest deleteRequest = new DeleteRequest("haoke", "house",  
GkpdE2gBCKv8opxuOj12");  
"
160  
ꢀ ꢀ ꢀ ꢀDeleteResponse response = this.client.delete(deleteRequest,  
RequestOptions.DEFAULT);  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
ꢀ ꢀ ꢀ ꢀSystem.out.println(response.status());// OK or NOT_FOUND  
ꢀ }  
ꢀ ꢀ/**  
ꢀ ꢀ * 更新数据  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testUpdate() throws Exception {  
ꢀ ꢀ ꢀ ꢀUpdateRequest updateRequest = new UpdateRequest("haoke", "house",  
"G0pfE2gBCKv8opxuRz1y");  
172  
173  
174  
175  
176  
177  
178  
179  
ꢀ ꢀ ꢀ ꢀMap<String, Object> data = new HashMap<>();  
ꢀ ꢀ ꢀ ꢀdata.put("title", "张江高科2");  
ꢀ ꢀ ꢀ ꢀdata.put("price", "5000");  
ꢀ ꢀ ꢀ ꢀupdateRequest.doc(data);  
ꢀ ꢀ ꢀ ꢀUpdateResponse response = this.client.update(updateRequest,  
RequestOptions.DEFAULT);  
1
80  
81  
ꢀ ꢀ ꢀ ꢀSystem.out.println("version -> " + response.getVersion());  
ꢀ }  
1
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
ꢀ ꢀ/**  
ꢀ ꢀ * 测试搜索  
ꢀ ꢀ *  
ꢀ ꢀ * @throws Exception  
ꢀ ꢀ */  
ꢀ ꢀ@Test  
ꢀ ꢀpublic void testSearch() throws Exception {  
ꢀ ꢀ ꢀ ꢀSearchRequest searchRequest = new SearchRequest("haoke");  
ꢀ ꢀ ꢀ ꢀsearchRequest.types("house");  
ꢀ ꢀ ꢀ ꢀSearchSourceBuilder sourceBuilder = new SearchSourceBuilder();  
ꢀ ꢀ ꢀ ꢀsourceBuilder.query(QueryBuilders.matchQuery("title", "拎包入住"));  
ꢀ ꢀ ꢀ ꢀsourceBuilder.from(0);  
ꢀ ꢀ ꢀ ꢀsourceBuilder.size(5);  
ꢀ ꢀ ꢀ ꢀsourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));  
ꢀ ꢀ ꢀ ꢀsearchRequest.source(sourceBuilder);  
ꢀ ꢀ ꢀ ꢀSearchResponse search = this.client.search(searchRequest,  
RequestOptions.DEFAULT);  
2
2
2
2
2
2
2
2
2
02  
03  
04  
05  
06  
07  
08  
ꢀ ꢀ ꢀ ꢀSystem.out.println("搜索到 " + search.getHits().totalHits + " 条数据.");  
ꢀ ꢀ ꢀ ꢀSearchHits hits = search.getHits();  
ꢀ ꢀ ꢀ ꢀfor (SearchHit hit : hits) {  
ꢀ ꢀ ꢀ ꢀ ꢀ ꢀSystem.out.println(hit.getSourceAsString());  
ꢀ ꢀ }  
ꢀ }  
09 }  
10  
北京市昌平区建材城西路金燕龙办公楼一层  
电话:400-618-9090  


results matching ""

    No results matching ""