ElasticSearch搜索引擎

You Know, for Search!

1. 简介

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

FIND_IN_SET 索引是否有效 索引type index_Elastic

基本概念

索引:index

索引index是存储document文档数据的结构,意义类似于关系型数据库中的数据库。

类型(逐渐被抛弃)

类型type也是用于存储document的逻辑结构,相对于index来说,type是index的下级,所以通常在面向有实际意义的数据时,index作为大类的划分,type作为小类的划分。比如如果把book书作为一个大类来建立index的话,那么书的类型(小说类、文学类、IT技术类等)就可以作为type。

文档:doc

文档的格式是json式的。
对于文档,有几个主要的标识信息:_index(插入到哪个索引中),_type(插入到哪个类型中),_id(文档的id是多少),在插入一个文档的时候,这几个信息也是必须的。

集群

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。

一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。一个节点只能通过指定某个集群的名字,来加入这个集群。

节点

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,

默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,

因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,

这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。

在一个集群里,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

分片与复制

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。

为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。

每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。

集群搭建


2. 下载与安装

1. ES安装

官网(国外): 比较慢

https://www.elastic.co/cn/downloads/elasticsearch

国内下载镜像网站

# ElasticSearch:
https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
# logstash:
https://mirrors.huaweicloud.com/logstash/?C=N&O=D
# kibana:
https://mirrors.huaweicloud.com/kibana/?C=N&O=D
# elasticsearch-analysis-ik:
https://github.com/medcl/elasticsearch-analysis-ik/releases
# cerebro:
https://github.com/lmenezes/cerebro/releases
window
  1. 解压可用

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_02

  1. 双击bin目录的elasticsearch.bat文件启动
  2. 访问默认端口:127.0.0.1:9200

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_03

  • 出现如上消息表示安装成功
Linux

2. 安装ElasticSearch-Head插件

  • Elasticsearch-head 是用于监控 Elasticsearch 状态的客户端插件,包括数据可视化、执行增删改查操作
  • 下载地址:ealstic-header
  • 解压到 es 同级目录下即可

FIND_IN_SET 索引是否有效 索引type index_Elastic_04

安装header插件需要grunt命令,因此需要安装node.js

步骤

  1. 安装node.js(网上找,不赘述)
  2. 安装cnpm阿里镜像:使用cnpm指令比npm速度更快,更稳定
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install express
  1. 安装grunt:打开cmd,输入命令 cd D:\Nodejs(你安装nodejs的路径) 进入Nodejs的根目录下,输入npm install -g grunt -cli进行安装grunt。
  2. 安装pathomjs:打开cmd进入你安装head的根目录下,然后执行命令:npm install 进行安装pathomjs
  3. 安装完成之后运行命令 grunt server,启动head服务(默认端口号是9100)

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_05

  1. 浏览器访问:localhost:9100(能连接到es说明安装成功)

FIND_IN_SET 索引是否有效 索引type index_Elastic_06

3. Kibana安装

步骤

  1. 下载地址:kibana华为云镜像地址(注意版本统一为7.6.1)
  2. 解压即用
  3. 汉化配置:打开kibana.yml将语言设置为中文
  4. 双击bin目录下的kibana.bat文件
  5. 访问:localhost:5601,安装策划你刚刚

4. 安装ik分词器插件

  1. 解压,放入 es 的plungs目录
  2. 重启es,如果加载了插件,即安装成功

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_07

4.1 ik_smart 最少切分
# get请求
GET _analyze
{
  "analyzer": "ik_smart",
  "text": "天生我材必有用"
}

FIND_IN_SET 索引是否有效 索引type index_Elastic_08

4.2 ik_max_word 最细粒度切分

穷尽词库

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "天生我材必有用"
}

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_09

4.3 自定义词典
  1. 自定义词典文件,将词典文件名配入ik的配置文件中

注意:文件编码要选择UTF-8 BOM,如果是ANIS则无法识别

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_10

  1. 重启es,可以看到加载到了拓展词典的日志

FIND_IN_SET 索引是否有效 索引type index_Elastic_11

  1. 测试

FIND_IN_SET 索引是否有效 索引type index_Elastic_12

FIND_IN_SET 索引是否有效 索引type index_Elastic_13

3. 配置文件

# 配置文件在config目录下
elasticsearch.yml:  用于配置Elasticsearch运行参数
jvm.options: 用于配置Elasticsearch JVM设置
log4j2.properties:  用于配置Elasticsearch日志
elasticsearch.yml配置

注意配置文件不能有中文,否则启动会报错闪退

cluster.name: salvation
node.name: wujz
http.port: 9200
#开启cors跨域访问支持
http.cors.enabled: true
#跨域访问允许的域名地址,(允许所有域名)以上使 用正则 http.cors.allow-origin: /.*/
http.cors.allow-origin: "*"

其他配置

cluster.name: es集群的名称,默认elasticsearch,建议配置方便管理。

node.name:节点名称,一台服务器就是一个节点,es默认会随机给出节点名称,建议配置,方便管理。

path.conf: 设置配置文件的存储路径,tarzip包安装默认在es根目录下的config文件夹,rpm安装默认在/etc/ elasticsearch

path.data: 设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径, 用逗号隔开

path.logs: 设置日志文件的存储路径,默认是es根目录下的 logs文件夹

path.plugins: 设置插件的存放路径,默认是es根目录下的plugins文件夹

bootstrap.memory_lock: 设置为true可以锁住es使用的内存,避免内存与swap分区交换数据

network.host: 设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体的 ip

http.port:设置对外服务的http端口,默认为9200。

transport.tcp.port:9300 集群结点之间通信端口

node.master:指定该节点是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新的master

node.data:指定该节点是否存储索引数据,默认为true

discovery.zen.ping.timeout:3s 设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。

discovery.zen.minimum_master_nodes:主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结点,那么这里要设置为2。

node.max_local_storage_nodes: 单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1.

#开启cors跨域访问支持
http.cors.enabled: true
#跨域访问允许的域名地址,(允许所有域名)以上使 用正则 http.cors.allow-origin: /.*/
http.cors.allow-origin: "*"

4. RestFul风格

4.1 风格简介

一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件.它主要是用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

基本操作说明

method

url

描述

PUT

localhost:9200/索引名称/索引名称/文档id

创建文档(指定文档id)

POST

localhost:9200/索引名称/索引类型

创建文档(产生随机id)

POST

localhost:9200/索引名称/索引类型/文档id/_update

{请求体}

修改文档

DELETE

localhost:9200/索引名称/索引类型/文档id

删除指定文档

GET

localhost:9200/索引名称/索引类型/文档id

获取指定文档数据

POST

localhost:9200/索引名称/索引类型/文档id/_serach

查询数据

4.2. 基本CRUD

3.1 添加操作

手动创建索引

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_14

创建并设置索引

PUT /list
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

设置索引类型

PUT /zhangsan
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "desc": {
        "type": "text"
      },
      "age": {
        "type": "long"
      }
    }
  }
}

字段类型

  1. 字符串类型:text, keyword
  1. text:表示支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
  2. keyword:表示不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
  1. 数值类型:long, integer, short, byte, double, float, half float, scaled fload
  2. 日期类型:date
  3. 布尔值类型:boolean
  4. 二进制类型:binart
  5. 等等…

注意: 类型映射不能操作已经存在的类型

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_15

添加文档

  • 指定id
PUT /salvation/zhangsan(或者_doc)/1
{
  "name": "zhangsan",
  "desc": "法外狂徒",
  "age": 23
}
  • 随机id:系统产生随机id
post /salvation/_doc
{
  "name": "张三",
  "desc": "球球你别学了",
  "age": 12
}

FIND_IN_SET 索引是否有效 索引type index_数据_16

注意:类型名称在7.x版本后已经过时,8.x版本后将被抛弃,因此**可写上自定义类型名称也可以用 “_doc “ 代替(占位)**

3.2 修改数据

1. PUT操作:相当于覆盖数据

put /salvation/zhangsan/1
{
  "name": "李六"
}

2. POST:相当于覆盖数据

POSt /salvation/zhangsan/1
{
  "name": "王五"
}

3. POST _update形式(推荐):局部更新,不覆盖

POST /salvation/zhangsan/1/_update
{
  "doc": {
    "name": "张三"
  }
}

注意:****POST _update命令格式不同于其他的命令,是将修改的内容放在“doc”中

参考链接

3.3 查询操作
# 获取各个节点详细信息
GET _cat/indices?v

FIND_IN_SET 索引是否有效 索引type index_Elastic_17

# 获取文档数据
GET /salvation/zhangsan/1

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_18

# 条件查询
GET /salvation/zhangsan/_search?q=name:张三

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_19

# 获取索引库指定类型所有文档数据
POST(GET) /salvation/zhangsan/_search

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_20

# 条件查询,查询条件放在query的match的json内容中
POST /salvation/zhangsan/_search
{
  "query": {
    "match": {
      "name": "张三"
    }
  }
}
3.4 删除操作
# 删除索引
DELETE /list
# 删除文档数据
DELETE /salvation/zhangsan/3

4.3 复杂查询

1. query关键字:

条件查询,查询条件放在query中

# 条件查询,查询条件放在query中
POST /salvation/zhangsan/_search
{
  "query": {
    "match": {
      "name": "张三"
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_21

2. match:条件匹配

match中的字段多条件查询用空格隔开,例如: 标签字段的多标签查询,满足一个条件即可被查到

3. _source:结果过滤
# 将你需要查询的字段,放在“_source”的中括号中
POST /salvation/zhangsan/_search
{
  "query": {
    "match": {
      "name": "张三"
    }
  },
  "_source": ["name", "age", "desc"]
}
4. sort :排序
# 年龄倒序查询 desc 降序  asc 升序
POST /salvation/zhangsan/_search
{
  "query": {
    "match": {
      "name": "张三"
    }
  },
  "sort": [
    {
      "age": "desc"
    }
  ]
}

FIND_IN_SET 索引是否有效 索引type index_Elastic_22

5. from ,size (分页查询)
# from 起始位置, size 数量
POST /salvation/zhangsan/_search
{
  "query": {
    "match": {
      "name": "张三"
    }
  },
  
  "from": 0, 
  "size": 2
}

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_23

6 bool 查询
6.1 must 必须

文档必须匹配 must 选项下的查询条件,相当于逻辑运算的 AND

# 查询条件放在中括号中,全部匹配才被搜索到
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "zhangsan"
          }
        },
        {
          "match": {
            "age": 19
          }
        }
      ]
    }
  }
}
6.2 should 应该

文档可以匹配 should 选项下的查询条件也可以不匹配,相当于逻辑运算的 OR

# 或条件查询
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "name": "zhangsan"
          }
        },
        {
          "match": {
            "age": 27
          }
        }
      ]
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_数据_24

6.3 must_not 一定没有

与 must 相反,匹配该选项下的查询条件的文档不会被返回;需要注意的是,must_not 语句 不会影响评分 ,它的作用只是将不相关的文档排除

# 查询不叫zhangsan或年龄不是27岁的
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "name": "zhangsan"
          }
        },
        {
          "match": {
            "age": 27
          }
        }
      ]
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_Elastic_25

6.4 filter 过滤器

和 must 一样,匹配 filter 选项下的查询条件的文档才会被返回,但是 filter 不评分 ,只起到过滤功能,与 must_not 相反

过滤条件

  • gt 大于
  • gte 大于等于
  • lt 小于
  • lte 小于等于
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "张三"
          }
        }
      ], 
      "filter": {
        "range": {
          "age": {
            "gte": 20,
            "lte": 40
          }
        }
      }  
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_数据_26

7. term 精确查询

多个词匹配精确查询

关于分词

  1. term:直接查询精确的
  2. match:会使用分词器解析 (先分析文档,然后通过分析的文档进行查询)

text, keyword的区别

  1. text:表示支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
  2. keyword:表示不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
# 多条件同时满足
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "name": "zhangsan"
          }
        },
        {
          "term": {
            "age": 19
          }
        }
      ]
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_Elastic_27

8. highlight 高亮查询
# 对查询结果的查询条件高亮显示
POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "zhangsan"
          }
        },
        {
          "match": {
            "desc": "法外狂徒"
          }
        }
      ]
    }
  }, 
  "highlight": {
    "fields": {
      "name": {},
      "desc":{}
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_elasticsearch_28

自定义高亮标签

POST /salvation/zhangsan/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "zhangsan"
          }
        },
        {
          "match": {
            "desc": "法外狂徒"
          }
        }
      ]
    }
  }, 
  "highlight": {
    "pre_tags": "<p class:\"key\", style='color: red'>", # 标签前缀
    "post_tags": "</p>",                                 # 标签后缀
    "fields": {
      "name": {},
      "desc":{}
    }
  }
}

FIND_IN_SET 索引是否有效 索引type index_FIND_IN_SET 索引是否有效_29


5. SpringBoot集成Es

依赖

<!-- ES 依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- json功能工具类 依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

配置类

@Configuration
public class ElasticSearchConfig {
    // 配置ES客户端
    @Bean(name = "restHighLevelClient")
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1", 9200, "http")
                )
        );
    }
}

测试

// 引入ES客户端
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;

// 设置索引库名字
private static final String INDEX = "salvation";
1. 创建索引
@Test
void createIndex() throws IOException {

    CreateIndexRequest request = new CreateIndexRequest("salvation");
    // RequestOptions.DEFAULT 表示默认请求参数
    CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
    logger.info("创建索引的响应:{}", JSONObject.toJSONString(response));
    logger.info("是否创建成功:{}",response.isAcknowledged());

}
// 创建索引的响应:{"acknowledged":true,"fragment":false,"shardsAcknowledged":true}
// 是否创建成功:true
2. 判断索引库是否存在
@Test
void existIndex() throws IOException {
    
    GetIndexRequest request = new GetIndexRequest(INDEX);
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    logger.info("GetIndexRequest: {}",request);
    logger.info("{}索引是否存在:{}", INDEX, exists);
    
}
// GetIndexRequest: org.elasticsearch.client.indices.GetIndexRequest@1a2909ae
// salvation索引是否存在:true
3. 删除索引
@Test
void deleteIndex() throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest("salvation");
    AcknowledgedResponse acknowledgedResponse = client.indices().delete(request, RequestOptions.DEFAULT);
    logger.info("acknowledgedResponse: {}" , JSONObject.toJSONString(acknowledgedResponse));
    logger.info("是否删除成功:{}", acknowledgedResponse.isAcknowledged());
}
//acknowledgedResponse: {"acknowledged":true,"fragment":false}
//是否删除成功:true
4. 添加文档数据
@Test
void addDocument() throws IOException {
    User user = new User("salvation", 19, 9527);
    IndexRequest indexRequest = new IndexRequest(INDEX);
    // 设置文档id
    indexRequest.id("1");
    //设置请求超时时间为1s
    indexRequest.timeout(TimeValue.timeValueSeconds(1));
    // XContentType.JSON 表示声明请求数据的类型
    indexRequest.source(JSONObject.toJSONString(user), XContentType.JSON);
    IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
    logger.info("创建文档的响应:{}", JSONObject.toJSONString(response));
}
// 创建文档的响应:{"fragment":false,"id":"1","index":"salvation","primaryTerm":2,"result":"CREATED","seqNo":0,"shardId":{"fragment":true,"id":-1,"index":{"fragment":false,"name":"salvation","uUID":"_na_"},"indexName":"salvation"},"shardInfo":{"failed":0,"failures":[],"fragment":false,"successful":1,"total":2},"type":"_doc","version":1}
5. 判断文档数据是否存在
@Test
void getDocument() throws IOException {
    GetRequest getRequest = new GetRequest(INDEX, "1");
    getRequest.id("1");
    getRequest.fetchSourceContext(new FetchSourceContext(false));
    getRequest.storedFields("_none_");
    boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
    logger.info("{}", exists); //true
}
6. 获取文档数据
@Test
void getDocument() throws IOException {
    GetRequest request = new GetRequest(INDEX, "1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    String string = response.getSourceAsString();
    logger.info("user: {}", JSONObject.parseObject(string, User.class));
}
// user: User(name=salvation, age=19, ID=9527)
7. 更新文档
@Test
void updateDocument() throws IOException {
    UpdateRequest request = new UpdateRequest(INDEX, "1");
    request.timeout(TimeValue.timeValueSeconds(1));
    User user = new User("zhangsan", 13, 1874);
    request.doc(JSONObject.toJSONString(user), XContentType.JSON);
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    logger.info("更新响应结果:{}", JSONObject.toJSONString(response));
}
//更新响应结果:{"fragment":false,"id":"1","index":"salvation","primaryTerm":2,"result":"UPDATED","seqNo":1,"shardId":{"fragment":true,"id":-1,"index":{"fragment":false,"name":"salvation","uUID":"_na_"},"indexName":"salvation"},"shardInfo":{"failed":0,"failures":[],"fragment":false,"successful":1,"total":2},"type":"_doc","version":2}
8. 删除文档
@Test
void deleteDocument() throws IOException {
    DeleteRequest request = new DeleteRequest(INDEX, "1");
    request.timeout(TimeValue.timeValueSeconds(1));
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
//响应结果:{"fragment":false,"id":"1","index":"salvation","primaryTerm":2,"result":"DELETED","seqNo":2,"shardId":{"fragment":true,"id":-1,"index":{"fragment":false,"name":"salvation","uUID":"_na_"},"indexName":"salvation"},"shardInfo":{"failed":0,"failures":[],"fragment":false,"successful":1,"total":2},"type":"_doc","version":3}
9. 批量处理
@Test
void bulkRequest() throws IOException {
    BulkRequest bulkRequest = new BulkRequest(INDEX);
    bulkRequest.timeout(TimeValue.timeValueSeconds(10));
    ArrayList<User> users = new ArrayList<>();
    users.add(new User("zhangsan 1", 13, 1874));
    users.add(new User("zhangsan 2", 13, 1874));
    users.add(new User("zhangsan 3", 13, 1874));
    users.add(new User("zhangsan 4", 13, 1874));
    users.add(new User("zhangsan 5", 13, 1874));

    for (int i = 0; i < users.size(); i++) {
        bulkRequest.add(new IndexRequest(INDEX)
                        .id(String.valueOf(i + 1))
                        .source(JSONObject.toJSONString(users.get(i)), XContentType.JSON));
    }

    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
10. 搜索
@Test
void searchRequest() throws IOException {
    SearchRequest searchRequest = new SearchRequest(INDEX);
    // 构建搜索条件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    // 查询条件, 可以使用QueryBuilders工具类实现
    // QueryBuilders.termQuery 精确
    // QueryBuilders.matchLLQuery() 匹配所有
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "zhangsan 1");
    sourceBuilder.query(termQueryBuilder);
    sourceBuilder.timeout(TimeValue.timeValueSeconds(1));
    searchRequest.source(sourceBuilder);
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
    logger.info("响应结果:{}", JSONObject.toJSONString(response));
}
//响应结果:{"clusters":{"fragment":true,"skipped":0,"successful":0,"total":0},"failedShards":0,"fragment":false,"hits":{"fragment":true,"hits":[],"maxScore":null,"totalHits":{"relation":"EQUAL_TO","value":0}},"numReducePhases":1,"profileResults":{},"shardFailures":[],"skippedShards":0,"successfulShards":1,"timedOut":false,"took":{"days":0,"daysFrac":7.523148148148148E-7,"hours":0,"hoursFrac":1.8055555555555555E-5,"micros":65000,"microsFrac":65000.0,"millis":65,"millisFrac":65.0,"minutes":0,"minutesFrac":0.0010833333333333333,"nanos":65000000,"seconds":0,"secondsFrac":0.065,"stringRep":"65ms"},"totalShards":1}

6. java爬虫 ----- Jsoup

待更新……