ElasticSearch6.5.4快速入门

一、ElasticSearch简介

1、概述

ES是一个使用Java语言并且基于Lucene编写的搜索引擎框架,他提供了分布式的全文搜索功能,提供了一个统一的基于RestFul风格的WEB接口,官方客户端也对多种语言都提供了相应的API。

Lucene: Lucene本身就是一个搜索引擎的底层。

分布式: ES主要是为了突出他的高扩展能力。

全文检索: 将一段词语进行分词,并且将分出的单个词语统一的放到一个分词库中,在搜索时,根据关键字去分词库中检索,找到匹配的内容。(倒排索引)

RestFul风格的WEB接口: 操作ES很简单,只需要发送一个HTTP请求,并且根据请求方式的不同,携带参数的不同,执行相应的功能。

2、ES和Solr比较

  1. Solr在查询死数据时,速度相对ES更快一些。但是数据如果是实时改变的,Solr的查询速度就会降低很多,ES查询的效率基本没有变化。
  2. Solr搭建基于需要依赖Zookeeper来帮忙管理。ES本身就支持集群的搭建,不需要第三方的介入。
  3. ES对现在云计算和大数据的支持特别好。

3、倒排索引

1、将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中。

2、当用户去查询数据时,会将用户的查询关键字进行分词。

3、然后去分词库中匹配内容,最终得到数据的id标识。

4、根据id标识去存放数据的位置拉取到指定的数据。

ElasticSearch6.5.4快速入门_第1张图片

二、ElasticSearch基于Docker方式安装

1、Kibana和IK分词器介绍

Kibana

kibana是一个针对Elasticsearch的开源分析及可视化平台,用于搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解,它操作简单,基于浏览器的用户界面可以快速创建仪表板实时显示ElasticSearch查询动态。版本必须与ES版本一致。

IK分词器

一个tokenizer(分词器)接收一个字符流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流。该分词器还负责记录各个term(词条)的顺序或psition位置(用于phrase短语和word proximity词近邻查询),以及term(词条)所代表的原始word(单词)的start(起始)和end(结束)的character offsets(字符偏移量)(用于高亮显示搜索的内容)。ES提供了很多内置的分词器,可以用来构建custm analyzers(自定义分词器)。

2、docker方式直接安装ES和Kibana

# 1、下载elasticsearch镜像
docker pull elasticsearch:6.5.4
# 2、下载kibana镜像
docker pull kibana:6.5.4
# 3、创建配置文件存放目录
mkdir -p /usr/local/docker/elasticsearch/config
# 4、创建数据存放目录
mkdir -p /usr/local/docker/elasticsearch/data
# 5、创建插件存放目录
mkdir -p /usr/local/docker/elasticsearch/plugins
# 6、追加配置文件内容,所有机器可以访问
echo "http.host: 0.0.0.0">>/usr/local/docker/elasticsearch/config/elasticsearch.yml
# 7、启动
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx128m" -v /usr/local/docker/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /usr/local/docker/elasticsearch/data:/usr/share/elasticsearch/data -v /usr/local/docker/elasticsearch/plugins:/usr/share/elasticsearch/plugins -d elasticsearch:6.5.4
# 8、发现报错,Caused by: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes
# 需要给每个文件夹加权限
chmod -R 777 /usr/local/docker/elasticsearch/
# 9、重启elasticsearch容器
docker start 容器ID
# 10、安装kibana
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.241.135:9200 -p 5601:5601 -d kibana:6.5.4
# 11、网址访问即可

3、docker-compose.yml方式安装ES和Kibana

# 1、新建一个目录
mkdir -p /usr/local/docker/elasticsearch/config
# 2、创建数据存放目录
mkdir -p /usr/local/docker/elasticsearch/data
# 3、创建插件存放目录
mkdir -p /usr/local/docker/elasticsearch/plugins
# 4、在镜像市场http://hub.daocloud.io找到elasticsearch和kibana镜像地址复制相应版本的地址
daocloud.io/library/elasticsearch:6.5.4
daocloud.io/library/kibana:6.5.4
# 5、检查是否安装docker-compose
docker-compose -v
# 6、如果没有安装则安装
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# 7、修改目录权限
chmod +x /usr/local/bin/docker-compose
# 8、新建一个docker-compose.yml文件
vim docker-compose.yml

配置docker-compose.yml文件

version: "3.1"
services:
  elasticsearch:
    # 镜像地址
    image: daocloud.io/library/elasticsearch:6.5.4
    restart: always
    container_name: 'elasticsearch'
    environment:
      # 设置运行内存
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - 9200:9200
    # 挂载外部文件
    volumes:
      - /usr/local/docker/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
      - /usr/local/docker/elasticsearch/data:/usr/share/elasticsearch/data
      - /usr/local/docker/elasticsearch/plugins:/usr/share/elasticsearch/plugins
  kibana:
    image: daocloud.io/library/kibana:6.5.4
    restart: always
    container_name: 'kibana'
    ports:
      - 5601:5601
    environment:
      - elasticsearch_url=http://192.168.241.135:9200
      # 汉化
      - I18N_LOCALE=zh-CN
    depends_on:
      - elasticsearch

执行启动

# 1、启动
docker-compose up -d
# 2、查看日志
docker-compose logs -f
# 3、网址访问即可

4、安装IK分词器插件

安装ES对应版本的分词器

https://github.com/medcl/elasticsearch-analysis-ik/releases

安装IK分词器

# 1、进入挂载目录下载对应版本的IK分词器(下载太慢,直接下载好通过XFTP工具上传到挂载目录)
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
# 2、进入容器内部查看对应的目录下是否存在下载的文件
docker exec -it 容器ID /bin/bash
# 3、解压文件夹
tar -zxvf elasticsearch-analysis-ik-6.5.4.zip
# 4、修改整个文件夹的权限
chmod -R 777 elasticsearch-analysis-ik-6.5.4/
# 5、进入容器内部操作
docker exec -it 容器ID /bin/bash
# 6、进入bin目录下执行命令查看是否装好
elasticsearch-plugin list
# 7、重启ES
docker restart 容器名

在Kibana中测试IK分词器是否有效

ElasticSearch6.5.4快速入门_第2张图片

三、ElasticSearch基于安装包方式安装

1、安装ES

1、下载地址

https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.tar.gz

2、解压

tar -zxvf elasticsearch-6.5.4.tar.gz

3、移动到安装位置

mv elasticsearch-6.5.4 /usr/local/program/

4、创建Elasticsearch用户

从5.0开始,ElasticSearch 安全级别提高了,不允许采用root帐号启动,所以我们要添加一个用户来启动ES和修改配置文件
# 创建ElaticSearch用户组
groupadd elasticsearch
# 创建用户
adduser es
# 设定密码
passwd es
# 将用户es添加到elasticsearch用户组
usermod -G elasticsearch es
# 添加权限
chown -R es elasticsearch-6.5.4

5、修改配置文件

1、取消如下注释,并修改为当前主机地址
# 进入配置文件目录
cd /usr/local/program/elasticsearch-6.5.4/config
# 编辑elasticsearch.yml文件
cluster.name: my-application
node.name: node-1
bootstrap.memory_lock: false
network.host: 0.0.0.0 # 所有都能访问,也可以设置为自己的服务器IP地址
http.port: 9200
discovery.zen.ping.unicast.hosts: ["0.0.0.0"]
discovery.zen.minimum_master_nodes: 1 #注意,因为本人目前是单节点,这里必须为1

# 新增如下配置
transport.tcp.port: 9300
transport.tcp.compress: true
bootstrap.system_call_filter: false
2、修改/etc/sysctl.conf文件,添加如下配置,否则会报错

在这里插入图片描述

# 新增配置
vm.max_map_count=262144
# 退出保存后执行如下命令
sysctl -p
3、修改/etc/security/limits.conf文件,添加如下配置,否则会报错
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
# 新增配置
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
# 退出保存后执行如下命令
sysctl -p
4、编辑jvm.options文件,修改es启动的jvm参数
-Xms256m # 默认值为1g,改为256m
-Xmx256m
5、启动ElasticSearch
# 切换用户
su es
# 查看当前用户
who am i
# 后台启动
./elasticsearch -d
# 查看是否启动成功
jps
6、浏览器访问

ElasticSearch6.5.4快速入门_第3张图片

2、安装Kibana

1、下载地址

https://artifacts.elastic.co/downloads/kibana/kibana-6.5.4-linux-x86_64.tar.gz
注意:Kibana需要与es版本对应

2、解压到安装位置

# 1、解压
tar -zxvf kibana-6.5.4-linux-x86_64.tar.gz
# 2、移动到安装位置并改名
mv kibana-6.5.4-linux-x86_64 /usr/local/program/kibana-6.5.4

3、修改配置文件

# 1、编辑kinaba.yml文件
vim /usr/local/program/kibana-6.5.4/config/kibana.yml
# 2、配置如下内容
server.port:5601
server.host:"localhost" # 由于es和kibana在同一台机器,可以使用localhost
elasticsearch.url:"localhost:9200"
kibana.index: ".kibana"
i18n.locale: "zh-CN"

4、启动Kibana

# 1、进入bin目录
cd bin
# 2、守护进程启动
nohup ./kibana &

5、查看Kibana进程

fuser -n tcp 5601

6、测试访问

ElasticSearch6.5.4快速入门_第4张图片

7、设置时区显示和时间格式

ElasticSearch6.5.4快速入门_第5张图片

3、安装IK分词器

1、下载对应版本的IK分词器

wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip

2、解压

# 1、安装unzip命令
yum update
yum install -y unzip
# 2、强行解压某文件到指定目录,解压压缩包到ik目录,并强行覆盖原有目录里内容
unzip -o elasticsearch-analysis-ik-6.5.4.zip -d ik

3、改名并移动到安装位置

IK分词器是一个插件,因此只需要将IK分词器放到ES目录的plugins文件夹下,然后重启ES即可

4、测试分词器是否安装成功

POST _analyze
{
  "analyzer": "ik_max_word",
  "text":"中国人"
}

ElasticSearch6.5.4快速入门_第6张图片

四、ElasticSearch的基本操作

1、ES的结构

1.1 索引Index、分片和备份

ES服务中,可以创建多个索引,类似MySql数据库

  1. 每一个索引默认被分成5片存储
  2. 每一个分片都会存在至少一个备份分片
  3. 备份分片默认不会帮助检索数据,当ES检索压力特别大的时候,备份分片才会帮助检索数据。
  4. 备份的分片必须放在不同的服务器中。

ElasticSearch6.5.4快速入门_第7张图片

1.2 类型Type

一个索引下,可以创建多个类型,类似MySql数据库中的多个表

  1. 根据版本的不同,类型的创建个数也不同。

ElasticSearch6.5.4快速入门_第8张图片

1.3 文档 Doc

一个类型下,可以有多个文档。类似MySql表中的多行数据

ElasticSearch6.5.4快速入门_第9张图片

1.4 属性 Field

一个文档下,可以包含多个属性,类似MySql表中的一行数据存在多个列

ElasticSearch6.5.4快速入门_第10张图片

2、操作ES的RestFul语法

2.1 GET 请求

# 查询索引信息
http://ip:port/index
# 查询指定的文档信息
http://ip:port/index/type/doc_id

2.2 POST 请求

# 查询文档,可以在请求体中添加json字符串来代替查询条件
http://ip:port/index/type/_search
# 修改文档,在请求体中指定json字符串代表要修改的具体信息
http://ip:port/index/type/doc_id/_update

2.3 PUT 请求

# 创建一个索引,需要在请求体中指定索引的信息,类型,结构
http://ip:port/index
# 代表创建索引时,指定索引文档存储的属性的信息
http://ip:port/index/type/_mappings

2.4 DELETE 请求

# 删除索引,会删除所有数据,类似删除数据库
http://ip:port/index
# 删除指定的文档,类似MySql中删除一条数据
http://ip:port/index/doc_id

3、索引的操作

3.1 创建索引

# 创建一个索引
PUT /persion
{
  "settings": {
    //number_of_shards:分片(默认5个),只能在创建 index 指定,后期无法修改
    "number_of_shards": 5,
    //number_of_replicas:备份(默认一个),可以在线修改
    "number_of_replicas": 1
  }
}

3.2 查看索引信息

3.2.1 Kibana 中直接查看

ElasticSearch6.5.4快速入门_第11张图片

3.2.2 请求方式查看

# 查看索引信息
GET /persion
# 返回数据
{
  "persion" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "creation_date" : "1597976845612",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "r0pobFOMSUaDhh7Lw7A6wA",
        "version" : {
          "created" : "6050499"
        },
        "provided_name" : "persion"
      }
    }
  }
}

3.3 删除索引

# 删除索引
DELETE /persion
# 返回数据
{
  "acknowledged" : true
}

4、ES中Field中可以指定的类型

字符串类型:

  1. text:用于全文搜索。会将当前Field就行分词。
  2. keyword:当前Field不会被分词。

数值类型:

  1. long
  2. integer
  3. short
  4. byte
  5. double
  6. float
  7. half_float:精度比float小一半
  8. scaled_float:根据一个long和scaled来表达一个浮点型,long-345,scaled-100,表示成3.45

时间类型:

  1. date类型,针对时间类型可以指定具体的格式

布尔类型:

  1. boolean类型,表达true和false

二进制类型:

  1. binary类型暂时支持Base64 encode string

范围类型:

  1. long_range:赋值时,无序指定具体的内容,只需要存储一个范围即可,指定gt,lt,gte,lte
  2. integer_range:同上
  3. double_range:同上
  4. float_range:同上
  5. date_range:同上
  6. ip_range:同上

经纬度类型:

  1. geo_point:用来存储经纬度的

ip类型:

  1. ip:可以存储IPV4和IPV6

其他数据类型可以参考官网: https://www.elastic.co/guide/en/elasticsearch/reference/6.5/mapping-types.html

5、创建索引并指定数据结构

# 创建索引,指定索引名称
PUT book
{
  "settings": {
    # 分片数
    "number_of_shards": 5,
    # 备分数
    "number_of_replicas": 1
  },
  # 指定数据结构
  "mappings": {
    # 类型 Type
    "novel":{
      # 文档存储的Field属性名称
      "properties":{
        # Field属性名
        "name":{
          # Field的类型
          "type":"text",
          # 分词器
          "analyzer":"ik_max_word",
          # 指定当前Field可以被作为查询的条件
          "index":true,
    	  # 是否需要额外存储(默认false"store":false
        },
        "author":{
          "type":"keyword"
        },
        "count":{
          "type":"long"
        },
        "onLine":{
          "type":"date",
          # 时间类型的格式化方式
          "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "content":{
          "type":"text",
          "analyzer":"ik_max_word"
        }
      }
    }
  }
}

6、添加新字段映射

## 追加属性
PUT book/novel/_mapping
{
  "properties": {
    "createTime":{
      "type": "date",
      "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
    }
  }
}

7、数据迁移

在ES中不允许对已经存在字段映射进行修改,若需要修改字段映射,需要创建新的索引进行数据迁移

# 1、在已经创建好新的索引,并设置好字段映射关系后,对数据进行迁移

# 2、数据迁移
POST _reindex
{
  "source": {
    "index": "原索引名",
    "type": "原类型"
  },
  "dest": {
    "index": "新索引名"
  }
}

8、文档操作之新建文档

文档在ES服务中的唯一标识,_index_type_id 三个内容为组合,锁定一个文档,操作是添加还是修改。

自动生成id

优点:自动生成的id太长,过于复杂,不利于操作

注意:使用POST请求,在不指定id的情况下会自动生成,但是使用PUT请求,不指定id情况下会报错

{
“error”: “Incorrect HTTP method for uri [/book/novel?pretty] and method [PUT], allowed: [POST]”,
“status”: 405
}

ElasticSearch6.5.4快速入门_第12张图片

自定义id

优点:方便好记、便于操作

注意:自定义id,可以使用POST和PUT请求,若不写id,PUT请求会报错

{
“error”: “Incorrect HTTP method for uri [/book/novel?pretty] and method [PUT], allowed: [POST]”,
“status”: 405
}

ElasticSearch6.5.4快速入门_第13张图片

9、文档操作之修改文档

覆盖式修改

操作已存在的id,result会变为update,会覆盖之前的数据,如果Field有缺漏,会将缺漏Field的数据置为空,造成数据丢失

ElasticSearch6.5.4快速入门_第14张图片

doc方式修改

可以设置修改部分Field信息,不会对所有Field造成影响

ElasticSearch6.5.4快速入门_第15张图片

查看数据

先要配置,才能查看(需要索引中存在数据才能配置

ElasticSearch6.5.4快速入门_第16张图片

ElasticSearch6.5.4快速入门_第17张图片

10、文档操作之删除文档

ElasticSearch6.5.4快速入门_第18张图片

五、SpringBoot集成ElasticSearch

1、SprinBoot连接ES的依赖

<properties>
    
    <elasticsearch.version>6.5.4elasticsearch.version>
properties>

<dependency>
    <groupId>org.elasticsearch.clientgroupId>
    <artifactId>elasticsearch-rest-high-level-clientartifactId>
    <version>6.5.4version>
dependency>

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>fastjsonartifactId>
    <version>1.2.49version>
dependency>

ElasticSearch6.5.4快速入门_第19张图片

2、配置类

@Configuration
public class ElasticSearchConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        //若有多个就配置多个
                        new HttpHost("192.168.241.135",9200,"http")
                )
        );
        return restHighLevelClient;
    }
}

3、测试是否配置成功

@SpringBootTest
class ElasticsearchApplicationTests {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    void t1() {
        //测试连接
        System.out.println(restHighLevelClient);
    }
}

ElasticSearch6.5.4快速入门_第20张图片

六、Java操作索引的API

1、创建索引

@SpringBootTest
class ElasticsearchApplicationTests {	
	@Test
    void t2() throws IOException {
        //索引的settings,设置分片、备份
        Settings.Builder settings = Settings.builder()
                .put("number_of_shards", 3)
                .put("number_of_replicas", 1);
        //索引的结构mappings
        XContentBuilder mappings = JsonXContent.contentBuilder()
                .startObject()
            		//属性集合
                    .startObject("properties")
            			//属性名
                        .startObject("name")
            				//指定属性类型
                            .field("type","text")
                        .endObject()
                        .startObject("age")
                            .field("type","integer")
                        .endObject()
                        .startObject("birthday")
                            .field("type","date")
                            .field("format","yyyy-MM-dd")
                        .endObject()
                    .endObject()
                .endObject();
        //将settings和mappings封装到一个Request对象,通过有参设置索引名,类似数据库名
        CreateIndexRequest request = new CreateIndexRequest("persion")
                .settings(settings)
            	//设置文档名,类似数据库中的表名
                .mapping("man",mappings);
        //通过restHighLevelClient对象连接ES并执行创建索引
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }
}

查看是否创建索引成功

ElasticSearch6.5.4快速入门_第21张图片

2、判断索引是否存在

@Test
void t3() throws IOException {
    //创建request对象
    GetIndexRequest request = new GetIndexRequest();
    //索引名
    request.indices("persion");
    //通过restHighLevelClient对象操作
    boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
    System.out.println("persion索引是否存在:" + exists);
}

3、删除索引

@Test
void t4() throws IOException {
    //创建request对象
    DeleteIndexRequest request = new DeleteIndexRequest();
    //索引名
    request.indices("book");
    //通过restHighLevelClient对象操作
    AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
    //输出状态
    System.out.println(response.isAcknowledged());
}

七、Java操作文档的API

1、添加文档

实体类:

@Data
/**无参构造*/
@NoArgsConstructor
/**有参构造*/
@AllArgsConstructor
public class Persion {
    /**忽略参数返回*/
    @JsonIgnore
    private Integer id;

    private String name;

    private Integer age;

    private Date birthDay;
}

添加文档:

@SpringBootTest
class ElasticsearchApplicationTests {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    void t5() throws IOException {
        //准备json数据
        Persion persion = new Persion(1,"张三",1,new Date());
        String json = JSON.toJSONString(persion);
        /**
         * 创建request对象
         * 参数说明:
         *      1、索引名,类似MySql中数据库
         *      2、类型名,类似要操作数据库中那一张表
         *      3、手动指定文档id
         */
        //创建request对象
        // 索引名 类型名
        IndexRequest request = new IndexRequest("persion","man",persion.getId().toString());
        request.source(json, XContentType.JSON);
        //通过restHighLevelClient对象操作
        IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        // 输出结果并返回
        System.out.println(JSON.toJSONString(response.getResult()));
    }
}

在Kibana中查看

ElasticSearch6.5.4快速入门_第22张图片

2、修改文档

@SpringBootTest
public class DocTest {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    
    @Test
    void t2() throws IOException{
        //创建一个Map,指定需要修改的内容
        Map<String,Object> map = new HashMap<>();
        map.put("name","李四");
        /**
         * 创建request对象,封装数据
         * 参数说明:
         *      1、索引名
         *      2、类型名
         *      3、文档id
         */
        UpdateRequest request = new UpdateRequest("persion","man","1");
        request.doc(map);
        //通过restHighLevelClient对象操作
        UpdateResponse update = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        //输出返回结果
        System.out.println(JSON.toJSONString(update.getResult()));
    }
}

查看结果:

ElasticSearch6.5.4快速入门_第23张图片

3、删除文档

@SpringBootTest
public class DocTest {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 文档删除
     * @throws IOException
     */
    @Test
    void t3() throws IOException{
        /**
         * 创建request对象,封装数据
         * 参数说明:
         *      1、索引名
         *      2、类型名
         *      3、文档id
         */
        DeleteRequest request = new DeleteRequest("persion","man","1");
        //通过restHighLevelClient对象操作
        DeleteResponse delete = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        //返回结果
        System.out.println(JSON.toJSONString(delete.getResult()));
    }
}

查看结果:

ElasticSearch6.5.4快速入门_第24张图片

4、批量新增

@SpringBootTest
public class DocTest {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 批量新增
     * @throws IOException
     */
    @Test
    void t4() throws IOException{
        //准备json数据
        Persion p1 = new Persion(1, "张三", 23, new Date());
        Persion p2 = new Persion(2, "李四", 22, new Date());
        Persion p3 = new Persion(3, "王五", 21, new Date());
        String json1 = JSON.toJSONString(p1);
        String json2 = JSON.toJSONString(p2);
        String json3 = JSON.toJSONString(p3);
        //创建BulkRequest对象,将数据封装进去
        BulkRequest request = new BulkRequest();
        request.add(new IndexRequest("persion","man",p1.getId().toString()).source(json1,XContentType.JSON));
        request.add(new IndexRequest("persion","man",p2.getId().toString()).source(json2,XContentType.JSON));
        request.add(new IndexRequest("persion","man",p3.getId().toString()).source(json3,XContentType.JSON));
        //通过restHighLevelClient对象操作
        BulkResponse bulk = restHighLevelClient.bulk(request,RequestOptions.DEFAULT);
        //输出
        System.out.println(JSON.toJSONString(bulk));
    }
}

查看结果:

ElasticSearch6.5.4快速入门_第25张图片

5、批量删除

@SpringBootTest
public class DocTest {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 批量删除
     * @throws IOException
     */
    @Test
    void t5() throws IOException{
        //创建BulkRequest对象,将数据封装进去
        BulkRequest request = new BulkRequest();
        request.add(new DeleteRequest("persion","man","1"));
        request.add(new DeleteRequest("persion","man","2"));
        request.add(new DeleteRequest("persion","man","3"));
        //通过restHighLevelClient对象操作
        BulkResponse bulk = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        //输出
        System.out.println(JSON.toJSONString(bulk));
    }
}

查看结果:

ElasticSearch6.5.4快速入门_第26张图片

八、文档查询

1、准备数据

实体类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SmsLogs {
    @JsonIgnore
    private Integer id;//编号
    private Date createDate;//创建日期
    private Date sendDate;//发送日期
    private String longCode;//发送的长号码
    private String mobile;//手机号
    private String corpName;//发送公司名称
    private String smsContent;//短信内容
    private Integer state;//状态 0-成功 1-失败
    private Integer operatorId;//运营商编号 1-移动 2-联通 3-电信
    private String province;//省份
    private String ipAddr;//下发服务器IP地址
    private Integer replyTotal;//短信状态报告返回时长(秒)
    private Integer fee;//费用
    @JsonIgnore
    public static String doc = "乌山镇,玉兰大陆第一山脉‘魔兽山脉’西方的芬莱王国中的一个普通小镇。朝阳初升,乌山镇这个小镇上依旧有着清晨的一丝清冷之气,只是小镇中的居民几乎都已经出来开始工作了,即使是六七岁的稚童,也差不多也都起床开始了传统性的晨练。乌山镇东边的空地上,早晨温热的阳光透过空地旁边的大树,在空地上留下了斑驳的光点。只见一大群孩子,目视过去估摸着差不多有一两百个。这群孩子分成了三个团队,每个团队都是排成几排,孩子们一个个都静静地站在空地上,面色严肃。这群孩子最北边一个团队的孩子大概六岁到八岁。而中间的一个团队,差不多是九岁到十二岁,最南边的则是一群十三岁到十六岁的少年。在这一大群孩子前方,便是三名壮硕的中年人,三个中年人都是穿着短背心以及粗布长裤。“想要成为一名伟大的战士,就必须从小刻苦锻炼。”为首的中年人背负着双手,昂着头颅冷然说道,那冷厉的目光更是朝最北边一个团队扫了过去,那群六七岁的孩子们一个个抿着嘴,乌溜溜的眼睛更是盯着这位中年人,丝毫不敢出声。为首的中年人名叫希尔曼,是乌山镇的拥有者‘巴鲁克家族’的护卫队队长。“你们都是普通人,不可能像那些大贵族一样有厉害的斗气密典修炼,想要出人头地,想要将来不被人瞧不起,你们就必须按照最古老、最简单、最基础的方法锻炼,锻炼身体、打熬力气,明白没有!”希尔曼目光扫向这一群孩子。“明白。”一群少年响亮地吼道。“很好。”希尔曼满意地冷漠点了点头,那些六七岁的孩子们眼中大多有着懵懵懂懂,而那些十几岁的少年们眼神都坚毅的很,因为他们明白希尔曼所说的含义。整个玉兰大陆几乎每一个男子都要从小努力锻炼,如果谁不刻苦训练,那他将来就会被人瞧不起!代表男人地位的,就是实力与金钱!一个没有实力的男人,就是女人也会瞧不起的。想要父母们引以为豪,想要女孩们崇拜,想要将来过的风光!就必须成为伟大的战士!他们都是平民,不可能修炼那些珍贵的斗气密典,他们唯一的方法就是从小锻炼身体、打熬力气!刻苦,努力!比那些贵族们花费更多的精力,花费更多的心血去努力!“早晨,朝阳升起,万物生机勃勃。此刻正是吸收天地的精华,提高我们身体潜力的最好时候,老规矩,双腿分开与肩同宽,双膝微微弯曲,双手收于腰部位置,成‘蕴气式’,做蕴气式的时候,要紧记‘集中注意力,保持心中平静,呼吸要自然’。”希尔曼冷漠说道。蕴气式,是锻炼身体的最简单也非常有效的办法,这是无数年来先辈积累出来的经验。顿时一两百个孩子都按照‘蕴气式’的姿势要求做好。“记住,集中注意力、心中平静、呼吸自然!”希尔曼走在少年群中,冷漠地说道。一眼看下去,很明显,南边的十几岁那个团队的少年们一个个都凝神静气、呼吸自然。同时一个个都做到了‘深、平、稳’。很显然在‘蕴气式’上都有了一些成就。不过再看北边六七岁的孩子,双膝弯的程度不同,双腿松松垮垮,明显没有力量,一点都不稳。希尔曼对着另外两位中年人说道:“你们分别负责南边和中央的两个团队,我去管理那些小孩子。”“是,队长。”那两名中年人当即应命,随后这二人分别仔细观看着中央、南边两个团队,时而还踢踢那些少年的腿,看那些少年是否站的够稳。而希尔曼则是朝北边那群六七岁的孩童走去,那些孩童们立即紧张了起来。“不好,大魔头来了。”一名大眼睛,叫‘哈德利’的金发孩童低声说道。希尔曼步入这一群孩童中央,看着这些孩子,希尔曼脸上冷漠,可是心中却感叹:“这些孩子实在太小了,无论是体能上或者心智上还远远不够,对他们的要求也不能太高。不过身体锻炼,还是从小培养的好,从小认真锻炼,将来上战场活命的机会才能更大。”而教导小孩子……诱导他们,吸引他们,是最有效的方法!强迫只能起反作用!“一个个站好了。”希尔曼冷哼一声。顿时一个个孩童挺起胸膛,目视前方。希尔曼嘴角升起一丝笑意,随后便走到最前面,脱去了身上的背心,身上那些肌肉线条让那些小孩子们一个个睁大了眼睛,连中央、南边的少年们也都羡慕地看着希尔曼身上的肌肉。除了那几近完美的肌肉线条,希尔曼那裸露的上半身有着刀痕、剑痕,各种伤痕足有数十道,所有的孩子都眼睛放光地看着那些伤痕。刀痕、剑痕,这是男人的勋章!他们的心底对于希尔曼都是非常崇拜的。希尔曼,一位伟大的六级战士,在生与死之中磨练出来的勇士";
}

创建索引:

@SpringBootTest
public class AddSource {
    private static final String index = "sms-logs-index";
    private static final String type = "sms-logs-type";

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    public void createIndex() throws  Exception{
        // 1.准备关于索引的setting
        Settings.Builder settings = Settings.builder()
                .put("number_of_shards", 3)
                .put("number_of_replicas", 1);

        // 2.准备关于索引的mapping
        XContentBuilder mappings = JsonXContent.contentBuilder()
                .startObject()
                    .startObject("properties")
                        .startObject("corpName")
                            .field("type", "keyword")
                        .endObject()
                        .startObject("createDate")
                            .field("type", "date")
                        .endObject()
                        .startObject("fee")
                            .field("type", "integer")
                        .endObject()
                        .startObject("ipAddr")
                            .field("type", "ip")
                        .endObject()
                        .startObject("longCode")
                            .field("type", "keyword")
                        .endObject()
                        .startObject("mobile")
                            .field("type", "keyword")
                        .endObject()
                        .startObject("operatorId")
                            .field("type", "integer")
                        .endObject()
                        .startObject("province")
                            .field("type", "keyword")
                        .endObject()
                        .startObject("replyTotal")
                            .field("type", "integer")
                        .endObject()
                        .startObject("sendDate")
                            .field("type", "date")
                        .endObject()
                        .startObject("smsContent")
                            .field("type", "text")
                            .field("analyzer", "ik_max_word")
                        .endObject()
                        .startObject("state")
                            .field("type", "integer")
                        .endObject()
                    .endObject()
                .endObject();
        // 3.将settings和mappings 封装到到一个Request对象中
        CreateIndexRequest request = new CreateIndexRequest(index)
                .settings(settings)
                .mapping(type,mappings);
        // 4.使用restHighLevelClient去连接ES
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println("response:" + JSON.toJSONString(response));
    }
}

添加文档数据:

@SpringBootTest
public class AddSource {
    private static final String index = "sms-logs-index";
    private static final String type = "sms-logs-type";

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    public void  bulkCreateDoc() throws  Exception{
        // 1.准备多个json 对象
        String longcode = "1008687";
        String mobile ="138340658";
        List<String> companies = new ArrayList<>();
        companies.add("腾讯课堂");
        companies.add("阿里旺旺");
        companies.add("海尔电器");
        companies.add("海尔智家公司");
        companies.add("格力汽车");
        companies.add("苏宁易购");
        List<String> provinces = new ArrayList<>();
        provinces.add("北京");
        provinces.add("重庆");
        provinces.add("上海");
        provinces.add("晋城");

        BulkRequest bulkRequest = new BulkRequest();
        for (int i = 1; i < 11 ; i++) {
            Thread.sleep(1000);
            SmsLogs s1 = new SmsLogs();
            s1.setId(i);
            s1.setCreateDate(new Date());
            s1.setSendDate(new Date());
            s1.setLongCode(longcode + i);
            s1.setMobile(mobile + 2 * i);
            s1.setCorpName(companies.get(i % 5));
            s1.setSmsContent(SmsLogs.doc.substring((i - 1) * 100,i * 100));
            s1.setState(i % 2);
            s1.setOperatorId(i % 3);
            s1.setProvince(provinces.get(i % 4));
            s1.setIpAddr("127.0.0." + i);
            s1.setReplyTotal(i * 3);
            s1.setFee(i * 6);
            String json1  = JSON.toJSONString(s1);
            bulkRequest.add(new IndexRequest(index,type,s1.getId().toString()).source(json1, XContentType.JSON));
            System.out.println("数据" + i + json1);
        }

        // 3.restHighLevelClient 执行
        BulkResponse responses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

        // 4.输出结果
        System.out.println(JSON.toJSONString(responses.getItems()));
    }
}

查看结果:

ElasticSearch6.5.4快速入门_第27张图片

2、term查询

term 查询是代表完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。

kibana中实现:

# term查询(类似 MySql 中:where province = 北京)
POST /sms-logs-index/sms-logs-type/_search
{
  "from":0,# 当前页
  "size":1,# 每一页显示条数
  "query": {
    "term": { # term条件
      "province": {
        "value": "北京" # 值
      }
    }
  }
}

java实现方式:

@SpringBootTest
public class QueryTest {
    private static final String index = "sms-logs-index";
    private static final String type = "sms-logs-type";
    
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    public void term() throws IOException {
        //创建SearchRequest对象
        SearchRequest request = new SearchRequest(index).types(type);
        //指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //分页参数当前页
        builder.from(0);
        //分页参数每页显示多少条
        builder.size(5);
        //term查询
        builder.query(QueryBuilders.termQuery("province","北京"));
        //将条件放入request中
        request.source(builder);
        //执行查询
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //提取数据(数据在hits里面)
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            //转为map集合
            Map<String, Object> result = hit.getSourceAsMap();
            System.out.println(result);
        }
    }
}

3、terms查询

terms 和 term 的查询机制是一样的,都不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应文档内容。

terms 是针对在一个字段包含多个值的时候使用,类似 MySql 中 in 查询方式

kibana中实现:

# terms查询 (类似 where province in('北京','上海')POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 10, 
  "query": {
    "terms": {
      "province": [
        "北京",
        "上海"
      ]
    }
  }
}

java实现方式:

@Test
public void terms() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //分页参数当前页
    builder.from(0);
    //分页参数每页显示多少条
    builder.size(10);
    //terms查询
    builder.query(QueryBuilders.termsQuery("province","北京","上海"));
    //将条件放入request中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据(数据在hits里面)
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

4、match查询

match查询属于高层查询,会根据查询的字段类型不一样,采用不同的查询方式。

  • 查询的内容如果是日期或者数值类型,会将你基于的字符串查询内容转换为日期或者数值对待。
  • 如果查询的内容是一个不能被分词的内容(keyword),match查询不会对你指定的查询关键字进行分词。
  • 如果查询的内容是一个可以被分词的内容(text),match会将你指定的查询内容根据一定方式进行分词,去分词库中匹配指定的内容。
  • match查询,底层实际就是多个term查询,将多个term查询的结果封装到一起。

kibana中实现:

# match查询,指定一个Field作为筛选条件
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": "最满意" # 会进行分词
    }
  }
}

java实现方式:

@Test
public void t4() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchQuery("smsContent","最满意"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

5、match_all查询

查询全部内容,不指定任何查询条件

kibana中实现:

# match_all查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match_all": {}
  }
}

java实现方式:

@Test
public void t3() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.matchAllQuery());
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

6、布尔match查询

基于一个 Field 匹配的内容,采用 and 或者 or 方式连接

kibana中实现:

# 布尔match查询 and方式连接
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "团队 最",
        "operator": "and"	# 查询出的内容既包含团队又包含最
      }
    }
  }
}

# 布尔match查询 or方式连接
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "团队 最",
        "operator": "or"	# 查询出的内容包含团队或者包含最
      }
    }
  }
}

java实现方式:

@Test
public void t5() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //and方式连接
    builder.query(QueryBuilders.matchQuery("smsContent","团队 最").operator(Operator.AND));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

@Test
public void t6() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //or方式连接
    builder.query(QueryBuilders.matchQuery("smsContent","团队 最").operator(Operator.OR));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

7、multiMatch查询

match 针对一个 Field 做检索,multi_match 针对多个 Field 进行检索。多个 Field 对应一个 text。

kibana中实现:

# multi_match查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "multi_match": {
      "query": "上",		# 指定text
      "fields": ["province","smsContent"]		# 指定 Fields
    }
  }
}

java实现方式:

@Test
public void t7() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.multiMatchQuery("上","province","smsContent"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

8、id查询

根据文档id进行查询

kibana中实现:

# 索引/类型/文档id
GET /sms-logs-index/sms-logs-type/1

java实现方式:

@Test
public void t8() throws IOException {
    //创建GetRequest对象
    GetRequest request = new GetRequest(index,type,"1");
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.multiMatchQuery("上","province","smsContent"));
    //执行查询
    GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
    //提取数据
    Map<String, Object> hits = response.getSource();
    System.out.println(hits);
}

9、ids查询

根据多个id查询,类似MySql中的where id in(id1,id2,id…)

kibana中实现:

POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "ids": {
      "values": ["1","2","3"]
    }
  }  
}

java实现方式:

@Test
public void t9() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.idsQuery().addIds("1","2","3"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

10、prefix查询

前缀查询,可以通过一个关键字去指定一个 Field 的前缀,从而查询到指定的文档数据。

kibana中实现:

# prefix 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "prefix": {
      "corpName": {
        "value": "阿里"
      }
    }
  }
}

java实现方式:

@Test
public void t10() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.prefixQuery("corpName","阿里"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

11、fuzzy查询

模糊查询,我们输入字符的大概,ES 就可以去根据输入的内容大概去匹配一下结果。

kibana中实现:

# fuzzy 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "fuzzy": {
      "corpName": {
        "value": "海尔电气",
        "prefix_length": 2		# 指定前面几个字符是不允许出现错误的
      }
    }
  }
}

java实现方式:

@Test
public void t11() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.fuzzyQuery("corpName","海尔电气").prefixLength(2));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

12、wildcard查询

统配查询,类似 MySql 中的 like 查询,可以在查询时,在字符串中指定通配符 * 和占位符 ?

kibana中实现:

# wildcard 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "wildcard": {
      "corpName": {
        "value": "海尔*"		# * 表示匹配所有,? 表示占位一个
      }
    }
  }
}

java实现方式:

@Test
public void t12() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.wildcardQuery("corpName","海尔?"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

13、range查询

范围查询,只针对数值类型,对某一个 Field 进行大于或者小于的范围指定。

kibana中实现:

# range 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "range": {
      "fee": {
        "gt": 10,		# gt:大于,gte:大于等于
        "lt": 20		# lt:小于,lte:小于等于
      }
    }
  }
}

java实现方式:

 @Test
public void t13() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.rangeQuery("fee").gt(10).lt(20));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

14、regexp查询

正则查询,通过编写的正则表达式去匹配内容

prefix,fuzzy,wildcard 和 regexp 查询效率相对 term 和 match 比较低,要求效率比较高时,避免去使用

kibana中实现:

# regexp 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "regexp": {
      "mobile": "138[0-9]{8}"
    }
  }
}

java实现方式:

@Test
public void t14() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.regexpQuery("mobile","138[0-9]{8}"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

15、深分页Scroll

ES 对 from + size是有限制的,from 和 size二者之和不能超过 1w

原理:

from + size 在ES查询数据的方式:

  • 第一步先将用户指定的关键词进行分词。
  • 第二步将词汇去分词库中进行检索,得到多个文档的id。
  • 第三步去各个分片中去拉取指定的数据。耗时较长
  • 第四步将数据根据score进行排序。耗时较长
  • 第五步根据from的值,将查询到的数据舍弃一部分。
  • 第六步返回结果。

Scroll + size 在ES查询数据的方式:

  • 第一步先将用户指定的关键词进行分词。
  • 第二步将词汇去分词库中进行检索,得到多个文档的id。
  • 第三步将文档的id存放在ES的上下文中。
  • 第四步根据你指定的size的个数去ES中检索指定个数的数据,拿完数据的文档id,会从上下文中移除。
  • 第五步如果需要下一页数据,直接去ES的上下文中,找后续内容。
  • 第六步循环第四步和第五步。

缺点:Scroll查询方式,不适合做实时的查询

kibana中实现:

# 执行scroll查询,返回第一页数据,并且将文档id信息存放在ES上下文中,指定生存时间60s
POST /sms-logs-index/sms-logs-type/_search?scroll=60s
{
  "query": {
    "match_all": {}
  }, 
  "size": 2,
  "sort": [				# 排序
    {
      "fee": {
        "order": "desc"
      }
    }
  ]
}

# 根据scroll查询下一页数据
POST /_search/scroll
{
  "scroll_id":"<根据第一步得到的scroll_id去指定>",
  "scroll":""
}

# 删除scroll在ES上下文中的数据
DELETE /_search/scroll/scroll_id

java实现方式:

@Test
public void t15() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定scroll信息,生存时间
    request.scroll(TimeValue.timeValueMinutes(1L));
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.size(4);
    builder.sort("fee", SortOrder.DESC);
    builder.query(QueryBuilders.matchAllQuery());
    request.source(builder);
    //获取返回结果scrollId,source
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    String scrollId = response.getScrollId();
    System.out.println("----首页----");
    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsMap());
    }
    while (true) {
        //循环 - 创建SearchScrollRequest
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        //指定scrollId的生存时间
        scrollRequest.scroll(TimeValue.timeValueMinutes(1L));
        //执行查询获取返回结果
        SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
        //判断是否查询到了数据,输出
        SearchHit[] hits = scrollResponse.getHits().getHits();
        if (hits != null && hits.length > 0){
            System.out.println("----下一页----");
            for (SearchHit hit : hits) {
                System.out.println(hit.getSourceAsMap());
            }
        } else {
            //判断没有查询到数据就退出循环
            System.out.println("----结束----");
            break;
        }
    }
    //创建ClearScrollRequst对象
    ClearScrollRequest clearRequest = new ClearScrollRequest();
    //指定scrollId
    clearRequest.addScrollId(scrollId);
    //删除scrollId
    ClearScrollResponse clearScrollResponse = restHighLevelClient.clearScroll(clearRequest, RequestOptions.DEFAULT);
    //输出
    System.out.println("是否删除成功:" + clearScrollResponse.isSucceeded());
}

16、delete-by-query

根据 term,match 等查询方式去删除大量的文档

如果需要删除的内容,是 index 下的大部分数据,此时不建议删除,推荐创建一个全新的 index,将保留的文档内容,添加到全新的索引中

kibana中实现:

# delete-by-query
POST /sms-logs-index/sms-logs-type/_delete_by_query
{
  "query":{
    "range":{
      "fee":{
        "lt":20
      }
    }
  }
}

java实现方式:

@Test
public void t16() throws IOException {
    //创建DeleteByQueryRequest
    DeleteByQueryRequest request = new DeleteByQueryRequest(index).types(type);
    //指定检索的条件
    request.setQuery(QueryBuilders.rangeQuery("fee").lt(20));
    //执行删除
    BulkByScrollResponse response = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
    //输出结果
    System.out.println(response.toString());
}

17、复合查询之bool查询

复合过滤器,将你的多个查询条件,以一定的逻辑组合在一起。

  • must:所有的条件,用 must 组合在一起,表示 and 的意思。
  • must_not:将 must_not 中的条件,全部都不能匹配,表示 not 的意思。
  • should:所有的条件,用 should 组合在一起,表示 or 的意思。

kibana中实现:

# 查询省份为北京或者上海
# 查询公司不是海尔电器
# smsContent中包含团队和最
# bool查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "province": {
              "value": "北京"
            }
          }
        },
        {
          "term": {
            "province": {
              "value": "上海"
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "corpName": {
              "value": "海尔电器"
            }
          }
        }
      ],
      "must": [
        {
          "match": {
            "smsContent": "团队"
          }
        },
        {
          "match": {
            "smsContent": "最"
          }
        }
      ]
    }
  }
}

java实现方式:

@Test
public void t17() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    boolQuery.should(QueryBuilders.termQuery("province","北京"));
    boolQuery.should(QueryBuilders.termQuery("province","上海"));
    boolQuery.mustNot(QueryBuilders.termQuery("corpName","海尔电器"));
    boolQuery.must(QueryBuilders.matchQuery("smsContent","团队"));
    boolQuery.must(QueryBuilders.matchQuery("smsContent","最"));

    builder.query(boolQuery);
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

18、复合查询之boosting查询

boosting 查询可以帮助我们去影响查询后的 score。

  • positive:只有匹配上 positive 的查询的内容,才会被放到返回的结果集中。
  • negative:只有匹配上和 positive 并且也匹配上了 negative,就可以降低这样的的文档 score。
  • negative_boost:指定系数,必须小于 1.0。

关于查询时,分数是如何计算的:

  • 搜索关键字在文档中出现的频次越高,分数就越高。
  • 指定的文档内容越短,分数就越高。
  • 在搜索时,指定的关键字也会被分词,这个分词的内容,被分词库匹配的个数越多,分数越高。

kibana中实现:

# boosting查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "smsContent": "中年人"
        }
      },
      "negative": {
        "match": {
          "smsContent": "希尔曼"
        }
      },
      "negative_boost": 0.5
    }
  }
}

java实现方式:

@Test
public void t18() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件,并指定系数
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoostingQueryBuilder boostingQuery = QueryBuilders.boostingQuery(
        QueryBuilders.matchQuery("smsContent", "中年人"),
        QueryBuilders.matchQuery("smsContent", "希尔曼")
    ).negativeBoost(0.5f);
    //将boostingQuery放到builder中
    builder.query(boostingQuery);
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

19、filter查询

query查询:根据查询条件,去计算文档的匹配度得到一个分数,并且根据分数进行排序,不会做缓存的。

filter查询:根据查询条件去查询文档,不去计算分数,而且filter会对经常被过滤的数据进行缓存。

kibana中实现:

# filter查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "corpName": "阿里旺旺"
          }
        },
        {
          "range":{
            "fee":{
              "gt":10
            }
          }
        }
      ]
    }
  }
}

java实现方式:

@Test
public void t19() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件,并指定系数
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    boolQuery.filter(QueryBuilders.termQuery("corpName","阿里旺旺"));
    boolQuery.filter(QueryBuilders.rangeQuery("fee").gt(10));
    //将boolQuery放到builder中
    builder.query(boolQuery);
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        //转为map集合
        Map<String, Object> result = hit.getSourceAsMap();
        System.out.println(result);
    }
}

20、高亮查询

高亮查询:就是将要查询的关键字,以一定的样式展示给用户,让用户知道为什么这个结果被检索出来。

高亮展示的数据,本身就是文档中的一个 Field,单独将 Field 以 highlight 的形式返回给你。

ES提供了一个 highlight 属性,和 query 同级别的。

  • fragment_size:指定高亮数据展示多少个字符(默认100个)
  • pre_tags:指定前缀标签,比如
  • post_tags:指定后缀标签,比如
  • fields:指定哪几个 Field 以高亮形式返回

kibana中实现:

# highlight查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": "不被人瞧不起"
    }
  },
  "highlight": {
    "fields": {
      "smsContent": {}
    },
    "pre_tags": ",
    "post_tags": "",
    "fragment_size": 10
  }
}

java实现方式:

@Test
public void t20() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件(高亮)
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //指定查询条件
    builder.query(QueryBuilders.matchQuery("smsContent","不被人瞧不起"));
    //指定高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("smsContent",10)
        .preTags("")
        .postTags("");
    //将highlightBuilder放到builder中
    builder.highlighter(highlightBuilder);
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //提取数据
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        System.out.println(hit.getHighlightFields().get("smsContent"));
    }
}

21、聚合查询

ES的聚合查询和MySQL的聚合查询类似,ES的聚合查询相比MySQL更强大的多,ES提供的统计数据的方式多种多样。

ES聚合查询的RESTful语法:

POST /index/type/_search
{
    "aggs": {
        "名字(agg)": {
            "agg_type": {
                "属性""值"
            }
        }
    }
}

1、去重计数查询

去重计数即 Cardinality,第一步先将返回的文档中的一个指定的 Field 进行去重,统计一共有多少一条

kibana中实现:

# 去重计数
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "cardinality": {
        "field": "province"
      }
    }
  }
}

java实现方式:

@Test
public void t21() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //指定使用的聚合方式
    builder.aggregation(AggregationBuilders.cardinality("agg").field("province"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //获取返回结果
    Cardinality agg = response.getAggregations().get("agg");
    System.out.println(agg.getValue());
}

2、范围统计

统计一定范围内出现的文档个数,比如:针对某一个 Field 的值在 0-100,100-200,200-300之间文档出现的个数分别是多少。

范围统计可以针对普通的数据,时间类型,ip类型都可以做相应的统计。

range,date_range,ip_range

kibana中实现:

# 数值方式范围统计
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "range": {
        "field": "fee",
        "ranges": [
          {
            "to": 20
          },
          {
            "from": 20,		# 包含当前值
            "to": 50		# 不包含当前值
          },
          {
            "from": 50
          }
        ]
      }
    }
  }
}
# 时间范围统计
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "date_range": {
        "field": "createDate",
        "format": "yyyy", 
        "ranges": [
          {
            "to": 2000
          },
          {
            "from": 2000
          }
        ]
      }
    }
  }
}
# ip范围统计
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "ip_range": {
        "field": "ipAddr",
        "ranges": [
          {
            "to": "127.0.0.10"
          },
          {
            "from": "127.0.0.15"
          }
        ]
      }
    }
  }
}

java实现数值范围统计方式:

@Test
public void t22() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //指定使用的聚合方式
    builder.aggregation(AggregationBuilders.range("agg").field("fee")
                        .addUnboundedTo(20)
                        .addRange(20,50)
                        .addUnboundedFrom(50));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //获取返回结果
    Range agg = response.getAggregations().get("agg");
    for (Range.Bucket bucket : agg.getBuckets()) {
        String key = bucket.getKeyAsString();
        Object from = bucket.getFrom();
        Object to = bucket.getTo();
        long docCount = bucket.getDocCount();
        System.out.println(String.format("key:%s,from:%s,to:%s,docCount:%s",key,from,to,docCount));
    }
}

22、统计聚合查询

统计聚合查询即(extended_stats),可以查询指定 Field 的最大值、最小值、平均值、平方和…

kibana中实现:

# 统计聚合查询
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "extended_stats": {
        "field": "fee"
      }
    }
  }
}

java实现方式:

@Test
public void t23() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //指定查询条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    //指定使用的聚合方式
    builder.aggregation(AggregationBuilders.extendedStats("agg").field("fee"));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //获取返回结果
    ExtendedStats agg = response.getAggregations().get("agg");
    double max = agg.getMax();
    double min = agg.getMin();
    System.out.println("最大值:" + max + "-----最小值:" + min);
}

其他的聚合查询方式查看官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/index.html

23、地图经纬度搜索

ES中提供了一个数据类型 geo_point,这个类型就是用来存储经纬度的。

kibana中实现:

# 创建一个索引,指定一个 name,location
PUT /map
{
  "settings": {
    "number_of_shards": 5, 
    "number_of_replicas": 1
  }, 
  "mappings": {
    "map": {
      "properties": {
        "name": {
          "type": "text"
        },
        "location": {
          "type": "geo_point"
        }
      }
    }
  }
}

# 添加经纬度
PUT /map/map/1
{
  "name": "天安门",
  "location": {
    "lon": 116.403981,
    "lat": 39.914492
  }
}

PUT /map/map/2
{
  "name": "海淀公园",
  "location": {
    "lon": 116.302509,
    "lat": 39.991152
  }
}

PUT /map/map/3
{
  "name": "北京动物园",
  "location": {
    "lon": 116.343184,
    "lat": 39.947468
  }
}

1、ES的地图检索方式

  • geo_distance:直线距离检索方式
  • geo_bounding_box:以两个点确定一个矩形,获取在矩形内的全部数据
  • geo_polygon:以多个点,确定一个多边形,获取多边形内的全部数据

kibana中实现:

# geo_distance
POST /map/map/_search
{
  "query": {
    "geo_distance": {
      "location": {		# 确定一个点的坐标
        "lon": 116.433733,
        "lat": 30.908404
      },
      "distance": 2000,			# 确定半径
      "distance_type": "arc"	# 指定形状为圆形
    }
  }
}
# geo_bounding_box
POST /map/map/_search
{
  "query": {
    "geo_bounding_box": {
      "location": {
        "top_left": {		# 左上角坐标点
          "lon": 116.326943,
          "lat": 30.95489
        },
        "bottom_right": {		# 右下角坐标点
          "lon": 116.347783,
          "lat": 30.939281
        }
      }
    }
  }
}
# geo_polygon
POST /map/map/_search
{
  "query": {
    "geo_bounding_box": {
      "location": {
        "points": [		# 指定多个点确定一个多边形
          {
            "lon": 116.298916,
            "lat": 39.99878
          },
          {
            "lon": 116.29561,
            "lat": 39.972576
          },
          {
            "lon": 116.327661,
            "lat": 30.984739
          }
        ]
      }
    }
  }
}

java实现方式:

@Test
public void t24() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest("map").types("map");
    //指定检索方式
    SearchSourceBuilder builder = new SearchSourceBuilder();
    List<GeoPoint> points = new ArrayList<>();
    //维度,经度与kibana中相反
    points.add(new GeoPoint(39.99878,116.298916));
    points.add(new GeoPoint(39.972576,116.29561));
    points.add(new GeoPoint(30.984739,116.327661));
    builder.query(QueryBuilders.geoPolygonQuery("location",points));
    //将条件放入request对象中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    //获取返回结果
    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsMap());
    }
}

你可能感兴趣的:(ElasticSearch,elasticsearch)