概述

RESTful

  • REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。
  • 在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI (Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。
  • 在 REST 样式的 Web 服务中,每个资源都有一个地址。资源本身都是方法调用的目标,方法列表对所有资源都是一样的。这些方法都是标准方法,包括 HTTP GET、POST、PUT、DELETE,还可能包括 HEAD 和 OPTIONS。简单的理解就是,如果想要访问互联网上的资源,就必须向资源所在的服务器发出请求,请求体中必须包含资源的网络路径,以及对资源进行的操作(增删改查)。

HTTP 客户端安装

  • 如果直接通过浏览器向 Elasticsearch 服务器发请求,那么需要在发送的请求中包含 HTTP 标准的方法,而 HTTP 的大部分特性且仅支持 GET 和 POST 方法。所以为了能方便地进行客户端的访问,可以使用 Postman 软件

  • Postman 是一款强大的网页调试工具,提供功能强大的 Web API 和 HTTP 请求调试。软件功能强大,界面简洁明晰、操作方便快捷,设计得很人性化。Postman 中文版能够发送任何类型的 HTTP 请求 (GET, HEAD, POST, PUT..),不仅能够表单提交,且可以附带任意类型请求体。

  • Postman 官网:https://www.getpostman.com

  • Postman 下载:https://www.getpostman.com/apps

ES 数据格式

  • Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便大家理解, 我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比

    Elasticsearch MySQL
    Index(索引) Database(数据库)
    Type(类型) Table(表)
    Index(文档) Row(行)
    Index(字段) Column(列)
  • ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。

  • 这里 Types 的概念已经被逐渐弱化(使用的过程中是通过索引找文档,不需要 Type 区分),Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type,Elasticsearch 7.X 中,Type 的概念已经被删除了。

  • ES 采用 JSON 作为文档序列化的格式,比如一条用户信息:

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "name": "John",
    "sex": "Male",
    "age": 25,
    "birthDate": "1990/05/01",
    "about": "I love to go rock climbing",
    "interests": ["sports", "music"]
    }

索引操作

创建索引

  • 对比关系型数据库,创建索引就等同于创建数据库

  • 在 Postman 中,向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/shopping

  • 请求后,服务器返回响应

    1
    2
    3
    4
    5
    6
    7
    // 注意:创建索引库的分片数默认 1 片,
    // 在 7.0.0 之前的 Elasticsearch 版本中,默认 5 片
    {
    "acknowledged"【响应结果】: true,
    "shards_acknowledged"【分片结果】: true,
    "index"【索引名称】: "shopping"
    }
  • 如果重复添加索引,会返回错误信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "error": {
    "root_cause": [
    {
    "type": "resource_already_exists_exception",
    "reason": "index [shopping/KhvcTABpTnu2aDZ2wuqRJQ] already exists",
    "index_uuid": "KhvcTABpTnu2aDZ2wuqRJQ",
    "index": "shopping"
    }
    ],
    "type": "resource_already_exists_exception",
    "reason": "index [shopping/KhvcTABpTnu2aDZ2wuqRJQ] already exists",
    "index_uuid": "KhvcTABpTnu2aDZ2wuqRJQ",
    "index": "shopping"
    },
    "status": 400
    }

查看所有索引

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/_cat/indices?v

    1
    2
    health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
    yellow open shopping KhvcTABpTnu2aDZ2wuqRJQ 1 1 0 0 208b 208b
  • 这里请求路径中的、_cat 表示查看的意思,indices 表示索引,所以整体含义就是查看当前 ES

  • 服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉,服务器响应结果如下

    表头 含义
    health 当前服务器健康状态:****green****(集群完整) ****yellow****(单点正常、集群不完整) red(单点不正常)
    status 索引打开、关闭状态
    index 索引名
    uuid 索引统一编号
    pri 主分片数量
    rep 副本数量
    docs.count 可用文档数量
    docs.deleted 文档删除状态(逻辑删除)
    store.size 主分片和副分片整体占空间大小
    pri.store.size 主分片占空间大小

查看单个索引

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "shopping【索引名】": {
    "aliases【别名】": {},
    "mappings【映射】": {},
    "settings【设置】": {
    "index【设置 - 索引】": {
    "creation_date【设置 - 索引 - 创建时间】": "1614265373911",
    "number_of_shards【设置 - 索引 - 主分片数量】": "1",
    "number_of_replicas【设置 - 索引 - 副分片数量】": "1",
    "uuid【设置 - 索引 - 唯一标识】": "eI5wemRERTumxGCc1bAk2A",
    "version【设置 - 索引 - 版本】": {
    "created": "7080099"
    },
    "provided_name【设置 - 索引 - 名称】": "shopping"
    }
    }
    }
    }
  • 查看索引向 ES 服务器发送的请求路径和创建索引是一致的。但是 HTTP 方法不一致。这里可以体会一下 RESTful 的意义,

  • 请求后,服务器响应结果如下:

删除索引

  • 在 Postman 中,向 ES 服务器发 DELETE 请求 :http://127.0.0.1:9200/shopping

    1
    2
    3
    {
    "acknowledged": true
    }
  • 重新访问索引时,服务器返回响应:索引不存在

文档操作

创建文档

  • 索引已经创建好了,接下来我们来创建文档,并添加数据。这里的文档可以类比为关系型数据库中的表数据,添加的数据格式为 JSON 格式

  • 在 Postman 中,向 ES 服务器发 POST 请求 (必须为 POST,不能是 PUT,否则会发生错误 ) :http://127.0.0.1:9200/shopping/_doc 请求体内容为:

    1
    2
    3
    4
    5
    6
    {
    "title": "小米手机",
    "category": "小米",
    "images": "http://www.gulixueyuan.com/xm.jpg",
    "price": 3999.0
    }
  • 服务器响应结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index【索引】": "shopping",
    "_type【类型-文档】": "_doc",
    "_id【唯一标识】": "Xhsa2ncBlvF_7lxyCE9G", // 可以类比为 MySQL 中的主键,随机生成
    "_version【版本】": 1,
    "result【结果】": "created", // 这里的 create 表示创建成功
    "_shards【分片】": {
    "total 【 分 片 - 总 数 】": 2,
    "successful【分片 - 成功】": 1,
    "failed【分片 - 失败】": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
    }
  • 上面的数据创建后,由于没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机生成一个。

自定义唯一性标识

查看文档

  • 查看文档时,需要指明文档的唯一性标识,类似于 MySQL 中数据的主键查询

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping/_doc/1

  • 查询成功后,服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "_index【索引】": "shopping",
    "_type【文档类型】": "_doc",
    "_id": "1",
    "_version": 1,
    "_seq_no": 1,
    "_primary_term": 2,
    "found【查询结果】": true, // true 表示查找到,false 表示未查找到
    "_source 【文档源信息】": {
    "title": "华为手机",
    "category": "华为",
    "images": "http://www.gulixueyuan.com/xm.jpg",
    "price": 3999.00
    }

    // 失败
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "found": false
    }

全量查询

  • 在 Postman 中,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping/_search

    • took: 查询时间
    • hits: 命中的数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    {
    "took": 213,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 3,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "Cfu0GX0BLjhFYSYEugih",
    "_score": 1.0,
    "_source": {
    "title": "小米手机",
    "category": "小米",
    "images": "http://www.gulixueyuan.com/xm.jpg",
    "price": 3999.0
    }
    },
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10086",
    "_score": 1.0,
    "_source": {
    "title": "华为手机",
    "category": "华为",
    "images": "http://www.gulixueyuan.com/xm.jpg",
    "price": 3999.0
    }
    },
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1000",
    "_score": 1.0,
    "_source": {
    "title": "小米手机",
    "category": "小米",
    "images": "http://www.gulixueyuan.com/xm.jpg",
    "price": 3999.0
    }
    }
    ]
    }
    }

全量修改文档

  • 和新增文档一样,输入相同的 URL 地址请求,如果请求体变化,会将原有的数据内容覆盖

  • 在 Postman 中,向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc/1

  • 请求体内容为:

    1
    2
    3
    4
    5
    6
    {
    "title": "华为手机",
    "category": "华为",
    "images": "http://www.gulixueyuan.com/hw.jpg",
    "price": 4999.0
    }
  • 修改成功后,服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10086",
    "_version【版本】": 2,
    "result【结果】": "updated", // updated 表示数据被更新
    "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 3,
    "_primary_term": 2
    }

局部修改字段

  • 修改数据时,也可以只修改某一给条数据的局部信息

  • 在 Postman 中,向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_update/1

  • 请求体内容为:

    1
    2
    3
    4
    5
    {
    "doc": {
    "title": "国产手机"
    }
    }
  • 修改成功后,服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10086",
    "_version": 3,
    "result": "updated",
    "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 4,
    "_primary_term": 2
    }
  • 根据唯一性标识,查询文档数据,文档数据已经更新

删除文档

  • 删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。

  • 在 Postman 中,向 ES 服务器发 DELETE 请求 :http://127.0.0.1:9200/shopping/_doc/1

  • 删除成功,服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10086",
    "_version【版本】": 4, // 对数据的操作,都会更新版本
    "result【结果】": "deleted", // deleted 表示数据被标记为删除
    "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 5,
    "_primary_term": 2
    }
  • 删除后再查询当前文档信息

    1
    2
    3
    4
    5
    6
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10086",
    "found": false
    }
  • 如果删除一个并不存在的文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "shopping",
    "_type": "_doc",
    "_id": "10087",
    "_version": 1,
    "result【结果】": "not_found", // not_found 表示未查找到
    "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 8,
    "_primary_term": 2
    }

条件删除文档

  • 一般删除数据都是根据文档的唯一性标识进行删除,实际操作时,也可以根据条件对多条数据进行删除

  • 首先分别增加多条数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "title":"小米手机",
    "category":" 小 米 ", "images":"http://www.gulixueyuan.com/xm.jpg", "price":4000.00
    }

    {
    "title":"华为手机",
    "category":" 华 为 ", "images":"http://www.gulixueyuan.com/hw.jpg", "price":4000.00
    }
  • 向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_delete_by_query

  • 请求体内容为:

    1
    2
    3
    4
    5
    6
    7
    {
    "query": {
    "match": {
    "price": 4000.0
    }
    }
    }
  • 删除成功后,服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "took【 耗 时 】": 1244,
    "timed_out【是否超时】": false,
    "total【总数】": 2,
    "deleted【删除数量】": 2,
    "batches": 1,
    "version_conflicts": 0,
    "noops": 0,
    "retries": {
    "bulk": 0,
    "search": 0
    },
    "throttled_millis": 0,
    "requests_per_second": -1.0,
    "throttled_until_millis": 0,
    "failures": []
    }

映射操作

  • 有了索引库,等于有了数据库中的 database。接下来就需要建索引库 (index) 中的映射了,类似于数据库 (database) 中的表结构 (table)。

  • 创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射 (mapping)。

创建映射

  • 在 Postman 中,向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/user/_mapping

  • 请求体内容为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "properties": {
    "name": {
    "type": "text",
    "index": true
    },
    "sex": {
    "type": "keyword",
    "index": true
    },
    "tel": {
    "type": "keyword",
    "index": false
    }
    }
    }
  • 相应结果

    1
    2
    3
    {
    "acknowledged": true
    }

映射数据说明

  • 字段名:任意填写,下面指定许多属性,例如:title、subtitle、images、price

  • type:类型,Elasticsearch 中支持的数据类型非常丰富,说几个关键的:

    • String 类型,又分两种:

      • text:可分词
      • keyword:不可分词,数据会作为完整字段进行匹配
    • Numerical:数值类型,分两类

      • 基本数据类型:long、integer、short、byte、double、float、
      • half_float 浮点数的高精度类型:scaled_float
    • Date:日期类型

    • Array:数组类型

    • Object:对象

  • index:是否索引,默认为 true,也就是说你不进行任何配置,所有字段都会被索引。

    • true:字段会被索引,则可以用来进行搜索
    • false:字段不会被索引,不能用来搜索
  • store:是否将数据进行独立存储,默认为 false

    • 原始的文本会存储在、_source 里面,默认情况下其他提取出来的字段都不是独立存储的,是从、_source 里面提取出来的。
    • 当然你也可以独立的存储某个字段,只要设置”store”: true 即可,获取独立存储的字段要比从、_source 中解析快得多,但是也会占用更多的空间,所以要根据实际业务需求来设置。
  • analyzer:分词器,这里的 ik_max_word 即使用 ik 分词器,后面会学习

查看映射

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/user/_mapping

  • 服务器响应结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "user": {
    "mappings": {
    "properties": {
    "name": {
    "type": "text"
    },
    "sex": {
    "type": "keyword"
    },
    "tel": {
    "type": "keyword",
    "index": false
    }
    }
    }
    }
    }

索引关联映射

  • 在 Postman 中,向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/user/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "settings": {},
    "mappings": {
    "properties": {
    "name": {
    "type": "text",
    "index": true
    },
    "sex": {
    "type": "text",
    "index": false
    },
    "age": {
    "type": "long",
    "index": false
    }
    }
    }
    }
  • 服务器响应结果如下:

    1
    2
    3
    4
    5
    {
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "user"
    }

高级查询

  • Elasticsearch 提供了基于 JSON 提供完整的查询 DSL 来定义查询定义数据 :

定义数据

  • 使用 post 请求添加数据

    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
    # POST /student/_doc/1001
    {
    "name":"zhangsan", "nickname":"zhangsan", "sex":"男",
    "age":30
    }
    # POST /student/_doc/1002
    {
    "name":"lisi",
    "nickname":"lisi",
    "sex":"男", "age":20
    }
    # POST /student/_doc/1003
    {
    "name":"wangwu", "nickname":"wangwu", "sex":"女",
    "age":40
    }
    # POST /student/_doc/1004
    {
    "name":"zhangsan1", "nickname":"zhangsan1", "sex":"女",
    "age":50
    }
    # POST /student/_doc/1005
    {
    "name":"zhangsan2", "nickname":"zhangsan2", "sex":"女",
    "age":30
    }
    # POST /student/_doc/1006
    {
    "name":"张三", "nickname":"zhangsan3", "sex":"女",
    "age":30
    }
    # POST /student/_doc/1007
    {
    "name":"张四", "nickname":"zhangsan4", "sex":"男",
    "age":30
    }

查询所有文档

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "query": {
    "match_all": {}
    }
    }
    # "query":这里的 query 代表一个查询对象,里面可以有不同的查询属性
    # "match_all":查询类型,例如:match_all(代表查询所有), match,term , range 等等
    # {查询条件}:查询条件会根据类型的不同,写法也有差异
  • 服务器响应结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "took【查询花费时间,单位毫秒】": 1116,
    "timed_out【是否超时】": false,
    "_shards【分片信息】": {
    "total【总数】": 1,
    "successful【成功】": 1,
    "skipped【忽略】": 0,
    "failed【失败】": 0
    },
    "hits【搜索命中结果】": {
    "total【搜索条件匹配的文档总数】": {
    "value【总命中计数的值】": 3,
    "relation【计数规则】": "eq #eq 表示计数准确, gte 表示计数不准确"
    },
    "max_score【匹配度分值】": 1,
    "hits【命中结果集合】": []
    }
    }

字段匹配查询

  • match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    {
    "query": {
    "match": {
    "name": "张三"
    }
    }
    }
  • 服务器响应结果为:(“张三” 分词后变成 “张” “三”),通过“张” 会将张四查询出来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    {
    "took": 601,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 2,
    "relation": "eq"
    },
    "max_score": 2.3117332,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1006",
    "_score": 2.3117332,
    "_source": {
    "name": "张三",
    "nickname": "zhangsan3",
    "sex": "女",
    "age": 30
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1007",
    "_score": 0.9477525,
    "_source": {
    "name": "张四",
    "nickname": "zhangsan4",
    "sex": "男",
    "age": 30
    }
    }
    ]
    }
    }

多字段匹配查询

  • multi_match 与 match 类似,不同的是它可以在多个字段中查询。

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "query": {
    "multi_match": {
    "query": "zhangsan",
    "fields": ["name", "nickname"]
    }
    }
    }
  • 服务器响应结果:

    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": 4,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.3862942,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.3862942,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    }
    }
    ]
    }
    }

关键字精确查询

  • term 查询,精确的关键词匹配查询,不对查询条件进行分词

  • 全文检索的字段类型需要设置为 text,但是 text 字段类型又不支持 term 等值查询,可以通过 fields 进行多字段配置

  • fields 主要使用场景:

    1. 对一个字段配置多个类型的 type 以应对不同的查询场景

    2. 对一个字段配置多个分词规则以支持多种全文检索规则

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      PUT my-index-000001
      {
      "mappings": {
      "properties": {
      "name": {
      "type": "text",
      "fields": { # fields 声明别名 keyword
      "keyword": {
      "type": "keyword",
      "analyzer": "english" # 可以指定采用的分词器
      }
      }
      }
      }
      }
      }

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 服务会返回 name = zhangsan 的数据
    {
    "query": {
    "term": {
    "name": {
    "value": "zhangsan"
    }
    }
    }
    }
    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
    # 使用中文查询不到结果
    {
    "query": {
    "term": {
    "name": "张三"
    }
    }
    }

    {
    "took": 1,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 0,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    }
    }
  • 会存在得不到结果的问题原因

    • 对于英文,ik 分词器会将大写字母转化为小写
    • 对于中文,IK 分词器是会将每一个中文都进行了分词的切割,所以你直接想查一整个词,或者一整句话是无返回结果的。
  • 解决 text 类型中文查询无结果,使用 filed.keyword 表示通过别名字段 keyword(类型要为 keyword) 全量匹配

    1
    2
    3
    4
    5
    6
    7
    {
    "query": {
    "term": {
    "name.keyword": "张三"
    }
    }
    }
  • 服务器响应结果:

    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": 2,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.6739764,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1006",
    "_score": 1.6739764,
    "_source": {
    "name": "张三",
    "nickname": "zhangsan3",
    "sex": "女",
    "age": 30
    }
    }
    ]
    }
    }

多关键字精确查询

  • terms 查询和 term 查询一样,但它允许你指定多值进行匹配

  • 如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于 mysql 的 in

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    {
    "query": {
    "terms": {
    "name": ["zhangsan", "lisi"]
    }
    }
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    {
    "took": 5,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 2,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1002",
    "_score": 1.0,
    "_source": {
    "name": "lisi",
    "nickname": "lisi",
    "sex": "男",
    "age": 20
    }
    }
    ]
    }
    }

指定查询字段

  • 默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在、_source 的所有字段都返回。如果我们只想获取其中的部分字段,我们可以添加 _source 的过滤

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "_source": ["name", "nickname"],
    "query": {
    "terms": {
    "nickname": ["zhangsan"]
    }
    }
    }
  • 服务器响应结果:

    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
    {
    "took": 6,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan"
    }
    }
    ]
    }
    }

过滤字段

  • 我们也可以通过:

    • includes:来指定想要显示的字段

    • excludes:来指定不想要显示的字段

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "_source": {
    "includes": ["name", "nickname"]
    },
    "query": {
    "terms": {
    "nickname": ["zhangsan"]
    }
    }
    }
  • 服务器响应结果:

    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
    {
    "took": 2,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan"
    }
    }
    ]
    }
    }
  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "_source": {
    "excludes": ["name", "nickname"]
    },
    "query": {
    "terms": {
    "nickname": ["zhangsan"]
    }
    }
    }
  • 服务器响应结果:

    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
    {
    "took": 4,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.0,
    "_source": {
    "sex": "男",
    "age": 30
    }
    }
    ]
    }
    }

组合查询

  • bool把各种其它查询通过must(必须 )、must_not(必须不)、should(应该)的方式进行组合

  • minimum_should_match 是最低匹配度, minimum_should_match 为 1, 表示 should 条件中,至少有一项符合。

  • shouldmust 一起用,should 会失效,加上 minimum_should_match 就可以了

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    // a && (b or c)
    {
    "query": {
    "bool": {
    "must": [
    {
    "match": {
    "age": 30
    }
    }
    ],
    "should": [
    {
    "term": {
    "sex": "女"
    }
    },
    {
    "term": {
    "name.keyword": "张三"
    }
    }
    ],
    "minimum_should_match": "1"
    }
    }
    }

    //(a && b) || (a && c)

    {
    "query": {
    "bool": {
    "should": [
    {
    "bool": {
    "must": [
    {"term": {"age": 30}},
    {"term": { "sex": "女"}}
    ]
    }
    },
    {
    "bool": {
    "must": [
    {"term": {"age": 30}},
    {"term": { "name.keyword": "张三"}}
    ]
    }
    }
    ]
    }
    }
    }

    // 在 must 中再嵌套一层 bool 来做 should 过滤
    // a && (b || c)
    {
    "query": {
    "bool": {
    "must": [
    {"term": {"age": {"value": 30}}},
    {
    "bool": {
    "should": [
    {"term": { "sex": "女"}},
    {"term": { "name.keyword": "张三"}}
    ]
    }
    }
    ]
    }
    }
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    {
    "took": 3,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 2,
    "relation": "eq"
    },
    "max_score": 3.2493405,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1006",
    "_score": 3.2493405,
    "_source": {
    "name": "张三",
    "nickname": "zhangsan3",
    "sex": "女",
    "age": 30
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1005",
    "_score": 1.5753641,
    "_source": {
    "name": "zhangsan2",
    "nickname": "zhangsan2",
    "sex": "女",
    "age": 30
    }
    }
    ]
    }
    }

范围查询

  • range 查询找出那些落在指定区间内的数字或者时间。

  • range 查询允许以下字符

    操作符 说明
    gt 大于>
    gte 大于等于>=
    lt 小于<
    lte 小于等于<=
  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "query": {
    "range": {
    "age": {
    "gte": 39,
    "lte": 40
    }
    }
    }
    }
  • 服务器响应结果:

    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": 1,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1003",
    "_score": 1.0,
    "_source": {
    "name": "wangwu",
    "nickname": "wangwu",
    "sex": "女",
    "age": 40
    }
    }
    ]
    }
    }

模糊查询

  • 返回包含与搜索字词相似的字词的文档。

  • 编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:

    • 更改字符(box → fox)
    • 删除字符(black → lack)
    • 插入字符(sic → sick)
    • 转置两个相邻字符(act → cat)
  • 为了找到相似的术语,fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。

  • 通过 fuzziness 修改编辑距离。一般使用默认值 AUTO,根据术语的长度生成编辑距离。

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "query": {
    "fuzzy": {
    "name": {
    "value": "zhangsan"
    }
    }
    }
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    {
    "took": 28,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 3,
    "relation": "eq"
    },
    "max_score": 1.8413742,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.8413742,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1004",
    "_score": 1.6112024,
    "_source": {
    "name": "zhangsan1",
    "nickname": "zhangsan1",
    "sex": "女",
    "age": 50
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1005",
    "_score": 1.6112024,
    "_source": {
    "name": "zhangsan2",
    "nickname": "zhangsan2",
    "sex": "女",
    "age": 30
    }
    }
    ]
    }
    }
  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "query": {
    "fuzzy": {
    "name": {
    "value": "zhangsan",
    "fuzziness": 2
    }
    }
    }
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    {
    "took": 11,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 3,
    "relation": "eq"
    },
    "max_score": 1.8413742,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.8413742,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1004",
    "_score": 1.6112024,
    "_source": {
    "name": "zhangsan1",
    "nickname": "zhangsan1",
    "sex": "女",
    "age": 50
    }
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1005",
    "_score": 1.6112024,
    "_source": {
    "name": "zhangsan2",
    "nickname": "zhangsan2",
    "sex": "女",
    "age": 30
    }
    }
    ]
    }
    }

单字段排序

  • sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。desc 降序,asc 升序。

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "query": {
    "fuzzy": {
    "name": "zhangsan"
    }
    },
    "sort": [
    {
    "age": {
    "order": "desc"
    }
    }
    ]
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    {
    "took": 9,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 3,
    "relation": "eq"
    },
    "max_score": null,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1004",
    "_score": null,
    "_source": {
    "name": "zhangsan1",
    "nickname": "zhangsan1",
    "sex": "女",
    "age": 50
    },
    "sort": [50]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": null,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    },
    "sort": [30]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1005",
    "_score": null,
    "_source": {
    "name": "zhangsan2",
    "nickname": "zhangsan2",
    "sex": "女",
    "age": 30
    },
    "sort": [30]
    }
    ]
    }
    }

多字段排序

  • 假定我们想要结合使用 age 和 _score 进行查询,并且匹配的结果首先按照年龄排序,然后按照相关性得分排序

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "query": {
    "match_all": {}
    },
    "sort": [
    {
    "age": {
    "order": "desc"
    }
    },
    {
    "_score": {
    "order": "desc"
    }
    }
    ]
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    {
    "took": 2,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1004",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan1",
    "nickname": "zhangsan1",
    "sex": "女",
    "age": 50
    },
    "sort": [50, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1003",
    "_score": 1.0,
    "_source": {
    "name": "wangwu",
    "nickname": "wangwu",
    "sex": "女",
    "age": 40
    },
    "sort": [40, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    },
    "sort": [30, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1005",
    "_score": 1.0,
    "_source": {
    "name": "zhangsan2",
    "nickname": "zhangsan2",
    "sex": "女",
    "age": 30
    },
    "sort": [30, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1006",
    "_score": 1.0,
    "_source": {
    "name": "张三",
    "nickname": "zhangsan3",
    "sex": "女",
    "age": 30
    },
    "sort": [30, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1007",
    "_score": 1.0,
    "_source": {
    "name": "张四",
    "nickname": "zhangsan4",
    "sex": "男",
    "age": 30
    },
    "sort": [30, 1.0]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1002",
    "_score": 1.0,
    "_source": {
    "name": "lisi",
    "nickname": "lisi",
    "sex": "男",
    "age": 20
    },
    "sort": [20, 1.0]
    }
    ]
    }
    }

高亮查询

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

  • Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式(高亮)的设置。

  • 在使用 match 查询的同时,加上一个 highlight 属性:

    • pre_tags:前置标签
    • post_tags:后置标签
    • fields:需要高亮的字段
    • title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空
  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "query": {
    "match": {
    "name": "zhangsan"
    }
    },
    "highlight": {
    "pre_tags": "<font color='red'>",
    "post_tags": "</font>",
    "fields": {
    "name": {}
    }
    }
    }
  • 服务器响应结果:

    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
    {
    "took": 3,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 1,
    "relation": "eq"
    },
    "max_score": 1.8413742,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1001",
    "_score": 1.8413742,
    "_source": {
    "name": "zhangsan",
    "nickname": "zhangsan",
    "sex": "男",
    "age": 30
    },
    "highlight": {
    "name": ["<font color='red'>zhangsan</font>"]
    }
    }
    ]
    }
    }

分页查询

  • from:当前页的起始索引,默认从 0 开始。 from = (pageNum - 1) * size , size:每页显示多少条

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "query": {
    "match_all": {}
    },
    "sort": [
    {
    "age": {
    "order": "desc"
    }
    }
    ],
    "from": 0,
    "size": 2
    }
  • 服务器响应结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    {
    "took": 6,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": [
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1004",
    "_score": null,
    "_source": {
    "name": "zhangsan1",
    "nickname": "zhangsan1",
    "sex": "女",
    "age": 50
    },
    "sort": [50]
    },
    {
    "_index": "student",
    "_type": "_doc",
    "_id": "1003",
    "_score": null,
    "_source": {
    "name": "wangwu",
    "nickname": "wangwu",
    "sex": "女",
    "age": 40
    },
    "sort": [40]
    }
    ]
    }
    }

聚合查询

  • 聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很多其他的聚合,例如取最大值、平均值等等。

  • 对某个字段取最大值 max , 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "max_age": {
    "max": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    // 服务器响应结果:
    {
    "took": 13,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "max_age": {
    "value": 50.0
    }
    }
    }
  • 对某个字段取最小值 min, 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "min_age": {
    "min": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    // 服务器响应结果:
    {
    "took": 2,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "min_age": {
    "value": 20.0
    }
    }
    }
  • 对某个字段求和 sum, 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "sum_age": {
    "sum": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    // 服务器响应结果:
    {
    "took": 7,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "sum_age": {
    "value": 230.0
    }
    }
    }
  • 对某个字段取平均值 avg, 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "avg_age": {
    "avg": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    // 服务器响应结果:
    {
    "took": 5,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "avg_age": {
    "value": 32.857142857142854
    }
    }
    }
  • 对某个字段的值进行去重之后再取总数,在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "distinct_age": {
    "cardinality": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    //服务器响应结果:
    {
    "took": 11,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "distinct_age": {
    "value": 4
    }
    }
    }
  • stats 聚合,对某个字段一次性返回 count,max,min,avg 和 sum 五个指标

  • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

    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
    {
    "aggs": {
    "stats_age": {
    "stats": {
    "field": "age"
    }
    }
    },
    "size": 0
    }

    // 服务器响应结果:
    {
    "took": 2,
    "timed_out": false,
    "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": {
    "value": 7,
    "relation": "eq"
    },
    "max_score": null,
    "hits": []
    },
    "aggregations": {
    "stats_age": {
    "count": 7,
    "min": 20.0,
    "max": 50.0,
    "avg": 32.857142857142854,
    "sum": 230.0
    }
    }
    }

桶聚合查询

  • 桶聚和相当于 sql 中的 group by 语句

  • terms 聚合,分组统计

    • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      {
      "aggs": {
      "age_groupby": {
      "terms": {
      "field": "age"
      }
      }
      },
      "size": 0
      }
    • 服务器响应结果:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      {
      "took": 8,
      "timed_out": false,
      "_shards": {
      "total": 1,
      "successful": 1,
      "skipped": 0,
      "failed": 0
      },
      "hits": {
      "total": {
      "value": 7,
      "relation": "eq"
      },
      "max_score": null,
      "hits": []
      },
      "aggregations": {
      "age_groupby": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
      {
      "key": 30,
      "doc_count": 4
      },
      {
      "key": 20,
      "doc_count": 1
      },
      {
      "key": 40,
      "doc_count": 1
      },
      {
      "key": 50,
      "doc_count": 1
      }
      ]
      }
      }
      }
  • 在 terms 分组下再进行聚合

    • 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      {
      "aggs": {
      // 根据年龄分组
      "age_groupby": {
      "terms": {
      "field": "age"
      },
      "aggs": {
      // 统计年龄的总和
      "sum_age": {
      "sum": {
      "field": "age"
      }
      }
      }
      }
      },
      "size": 0
      }
    • 服务器响应结果:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      {
      "took": 4,
      "timed_out": false,
      "_shards": {
      "total": 1,
      "successful": 1,
      "skipped": 0,
      "failed": 0
      },
      "hits": {
      "total": {
      "value": 7,
      "relation": "eq"
      },
      "max_score": null,
      "hits": []
      },
      "aggregations": {
      "age_groupby": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
      {
      "key": 30,
      "doc_count": 4,
      "sum_age": {
      "value": 120.0
      }
      },
      {
      "key": 20,
      "doc_count": 1,
      "sum_age": {
      "value": 20.0
      }
      },
      {
      "key": 40,
      "doc_count": 1,
      "sum_age": {
      "value": 40.0
      }
      },
      {
      "key": 50,
      "doc_count": 1,
      "sum_age": {
      "value": 50.0
      }
      }
      ]
      }
      }
      }

时间范围查询

时间的数学表达式

  • Elasticsearch 中时间可以表示为now, 也就是系统当前时间,也可以是以||结尾的日期字符串表示。

  • 在日期之后,可以选择一个或多个数学表达式:

    • +1h —— 加 1 小时;

    • -1d —— 减 1 天;

    • /d —— 四舍五入到最近的一天。

  • 下面是 Elasticsearch 支持数学表达式的时间单位:

    表达式 含义 表达式 含义
    y M
    w 星期 d
    h 小时 H 小时
    m 分钟 s
  • 说明:假设系统当前时间now = 2018-10-01 12:00:00 :

    • now+1h: now 的毫秒值 + 1 小时,结果是:2018-10-01 13:00:00.
    • now-1h: now 的毫秒值 - 1 小时,结果是:2018-10-01 11:00:00.
    • now-1h/d: now 的毫秒值 - 1 小时,然后四舍五入到最近的一天的起始,结果是:2018-10-01 00:00:00.
    • 2018.10.01||+1M/d: 2018-10-01的毫秒值 + 1 月,再四舍五入到最近一天的起始,结果是:2018-11-01 00:00:00.

关于时间的四舍五入

  • 对日期中的日、月、小时等 进行四舍五入时,取决于范围的结尾是包含 (include) 还是排除 (exclude).

    • (exclude) 向上舍入:移动到舍入范围的最后一毫秒;
    • (include) 向下舍入:一定到舍入范围的第一毫秒。
  • 举例说明:

    • "gt": "2018-12-18||/M" —— 大于日期,需要向上舍入,结果是2018-12-31T23:59:59.999, 也就是不包含整个 12 月。
    • "gte": "2018-12-18||/M" —— 大于或等于日期,需要向下舍入,结果是 2018-12-01, 也就是包含整个 12 月。
    • "lt": "2018-12-18||/M" —— 小于日期,需要向上舍入,结果是2018-12-01, 也就是不包含整个 12 月。
    • "lte": "2018-12-18||/M" —— 小于或等于日期,需要向下舍入,结果是2018-12-31T23:59:59.999, 也就是包含整个 12 月。
  • 需求:查询网站中最近一天发布的博客:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET website/_search
    {
    "query": {
    "range": {
    "post_date": {
    "gte": "now-1d/d", // 当前时间的上一天,四舍五入到最近的一天
    "lt": "now/d" // 当前时间,四舍五入到最近的一天
    }
    }
    }
    }

日期格式化范围查询

  • 格式化日期查询时,将默认使用日期 field 中指定的格式进行解析,当然也可以通过 format 参数来覆盖默认配置。

  • 注意:如果日期中缺失了部分年、月、日,缺失的部分将被填充为 unix 系统的初始值,也就是 1970 年 1 月 1 日。

  • 比如,将dd指定为 format, 像"gte": 10将转换为1970-01-10T00:00:00.000Z.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    GET website/_search
    {
    "query": {
    "range": {
    "post_date": {
    "gte": "2/1/2018",
    "lte": "2019",
    "format": "dd/MM/yyyy||yyyy"
    }
    }
    }
    }

时区范围查询

  • 如果日期 field 的格式允许,也可以通过在日期值本身中指定时区,从而将日期从另一个时区的时间转换为 UTC(世界协调时间)时间,或者为其指定特定的time_zone参数。

  • CST(中国标准时间)= GMT(格林威治标准时间) + 8 = UTC(世界协调时间) + 8

  • es 默认存储时间的格式是 UTC 时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET website/_search
    {
    "query": {
    "range": {
    "post_date": {
    "gte": "2018-01-01 00:00:00",
    "lte": "now",
    "format": "yyyy-MM-dd hh:mm:ss",
    "time_zone": "+1:00"
    // "time_zone": "Asia/Shanghai"
    }
    }
    }
    }
  • 另外需要注意的是,now是不受time_zone影响的。

  • ES 中的日期类型必须按照 UTC 时间格式存储,所以,上述的2018-01-01 00:00:00将被转换为2017-12-31T23:00:00 UTC.

  • 参考资料:Elasticsearch 官方文档 - Range Query