# 一.系统命令

# 1.获取基本信息

用于获取 Elasticsearch 集群的根节点信息。这个请求通常用于检查 Elasticsearch 服务是否正常运行,以及获取一些基本的状态信息。

get /
1
  1. 名称:集群的名称。
  2. 集群 UUID:集群的唯一标识符。
  3. 版本:Elasticsearch 服务的版本号。
  4. 标签:集群的标签信息,如果有的话。
  5. HTTP 地址:集群可以访问的 HTTP 地址列表。
  6. 构建:关于 Elasticsearch 构建的详细信息,包括构建哈希、构建日期等。
{
  "name": "node-1",
  "cluster_name": "my-application",
  "cluster_uuid": "gmjBLVYEQFO6ZR2bveXFig",
  "version": {
    "number": "7.12.0",
    "build_flavor": "default",
    "build_type": "tar",
    "build_hash": "78722783c38caa25a70982b5b042074cde5d3b3a",
    "build_date": "2021-03-18T06:17:15.410153305Z",
    "build_snapshot": false,
    "lucene_version": "8.8.0",
    "minimum_wire_compatibility_version": "6.8.0",
    "minimum_index_compatibility_version": "6.0.0-beta1"
  },
  "tagline": "You Know, for Search"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2._cluster 获取 health

GET /_cluster/health
1

解释:Status:集群状态。

  • Green 所有分片可用。
  • Yellow 所有主分片可用。
  • Red 主分片不可用,集群不可用。
{
  "cluster_name": "my-application",
  "status": "yellow",
  "timed_out": false,
  "number_of_nodes": 1,
  "number_of_data_nodes": 1,
  "active_primary_shards": 8,
  "active_shards": 8,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 1,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 88.88888888888889
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3._cat 获取 health

GET /_cat/health?v
1

image-20230509195424757

  • green:每个索引的 primary shard 和 replica shard 都是 active 状态的.
  • yellow:每个索引的 primary shard 都是 active 状态的,但是部分 replica shard 不是 active 状态,处于不可用的状态
  • red:不是所有索引的 primary shard 都是 active 状态的,部分索引有数据丢失了

# 4.命令区别

GET /_cluster/health 和 GET /_cat/health?v 区别

GET /_cluster/healthGET /_cat/health?v 都是用于获取 Elasticsearch 集群健康状态的 API,但它们有一些区别:

  1. 用途

    • _cluster/health:这是一个监控集群整体健康状态的 API,它可以提供集群级别的健康状态,包括索引的健康状态。它通常用于自动化脚本和应用程序中。
    • _cat/health?v:这是一个用于人类阅读的 API,它提供了易于阅读的表格格式输出,通常用于命令行或 Kibana 控制台中快速检查集群状态。
  2. 输出格式

    • _cluster/health:返回 JSON 格式的数据,适合程序解析和自动化处理。
    • _cat/health?v:返回纯文本格式的数据,包含列标题,更易于人类阅读。
  3. 功能

    • _cluster/health:除了提供集群健康状态,还可以等待直到集群达到期望的健康状态,并且可以指定返回分片级别的健康信息。
    • _cat/health?v:提供了集群健康状态的快照,包括节点总数、数据节点数、分片数等信息,但不提供等待功能。
  4. 参数

    • _cluster/health:支持多个参数,如 wait_for_statustimeoutlevel 等,可以细化请求和控制返回的信息。
    • _cat/health?v:参数较少,v 参数用于显示列标题,ts 参数用于控制是否显示时间戳。
  5. 适用场景

    • _cluster/health:适合用于应用程序和自动化脚本中,需要详细和程序可操作的数据。
    • _cat/health?v:适合用于快速检查和监控集群状态,以及在命令行界面中进行故障排除。

# 5.查看索引

GET /_cat/indices?v
1

image-20230509195632464

# 6.主键 id

手动 id

文档的 ID 被指定为 2

当这个请求被发送到 Elasticsearch 服务器后,服务器会处理这个请求,将提供的 JSON 数据存储在指定的索引和文档 ID 下。如果索引 book 不存在,Elasticsearch 会根据默认设置创建它。如果文档 ID 2 已经存在,则该文档将被更新;如果不存在,将创建一个新的文档。

PUT /book/2
{
    "id":1,
    "title":"这是一11文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7
8

自动 id

POST /book/
{
    "id":1,
    "title":"这是一11文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7
8

自动id特点:长度为 20 个字符,URL 安全,base64 编码,GUID,分布式生成不冲突

{
  "_index": "book",
  "_type": "_doc",
  "_id": "Qk9rAIgBMxAD_MReKtAQ",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 8,
  "_primary_term": 1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 二.索引 crud

# 1.创建索引

在 Elasticsearch 中,当你在 API 请求的末尾添加 ?pretty 参数时,它会让 Elasticsearch 美化(pretty-print)返回的 JSON 响应,使其更易于阅读和调试。这个参数不会影响返回数据的实际内容,只是让 JSON 格式的输出更加易读。

PUT /demo_index?pretty
1

{ "acknowledged": true, "shards_acknowledged": true, "index": "demo_index" }

# 2.删除索引

DELETE /demo_index?pretty
DELETE db01_V1_20240911
1
2

{ "acknowledged": true }

# 3.先获取再删除

GET /_cat/indices?v

DELETE db01_v1_20240903-114316_
DELETE db01_v1_xingsen-poc-gpt-4o_
1
2
3
4

# 4.插入文档

put /blog_index/2
{
    "title":"这是一篇文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7

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

# 5.查询文档

get /blog_index/_doc/1
1

{ "_index": "blog_index", "_type": "_doc", "_id": "1", "_version": 1, "_seq_no": 0, "_primary_term": 1, "found": true, "_source": { "id": 1, "title": "这是一篇文章", "content": "xxxxx", "comment": "备注信息", "mobile": "13344556677" } }

# 6.图书 crud 例子

创建 book 索引

PUT /book
1

插入数据

PUT /book/1
{
    "id":1,
    "title":"这是一篇文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7
8

查询数据

GET /book/1
1

使用 put 全量替换

实质:日文档的内容不会立即删除,只是标记为 deleted。适当的时机,集群会将这些文档删除。

PUT /book/1
{
    "id":1,
    "title":"这是一11文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7
8

局部更新

  1. es 内部获取旧文档
  2. 将传来的文档 field 更新到旧数据(内存)
  3. 将旧文档标记问 delete
  4. 创建新文档
POST /book/_doc/1/_update
{
  "doc": {
    "title": "这是一333文章"
  }
}
1
2
3
4
5
6

noop:

  • 多次执行"result" : "noop’
  • no operation

局部更新

POST /book/_update/1
{
  "doc": {
    "title": "这是一3333333444555文章"
  }
}
1
2
3
4
5
6

删除数据

DELETE /book/_doc/1
1

# 三.高阶使用

# 1.属性分析

  • 版本号
  • 删除的时候做标记,使用的是懒删除
  • _id 分为手动 id 和默认 id
{
  "_index": "book",
  "_type": "_doc",
  "_id": "1",
  "_version": 6,
  "_seq_no": 7,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "id": 1,
    "title": "这是一文章",
    "content": "xxxxx",
    "comment": "备注信息",
    "mobile": "13344556677"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2._source 字段

含义:插入数据时的所有字段和值。在 get 获取数据时,在 source 字段中原样返回。

GET /book/_doc/1
1

定制返回:

GET /book/_doc/1?_source_includes=id,title
1
{
  "_index": "book",
  "_type": "_doc",
  "_id": "1",
  "_version": 6,
  "_seq_no": 7,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "id": 1,
    "title": "这是一文章"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.强制创建

为防止覆盖原有数据,我们在新增时,设置为强制创建,不会覆盖原有文档。

PUT /index/_doc/1/_create
1
PUT /read_index/_doc/1/_create
{
    "id":1,
    "title":"这是一11文章",
    "content":"xxxxx",
    "comment":"备注信息",
    "mobile":"13344556677"
}
1
2
3
4
5
6
7
8
{
  "_index": "read_index",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.脚本使用

#插入数据
PUT /test_index/_doc/6
{
  "num": 0
}

#执行脚本
POST /test_index/_doc/6/_update
{
  "script": "ctx._source.num+=1"
}

#查询数据
GET /test_index/_doc/6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "_index": "test_index",
  "_type": "_doc",
  "_id": "6",
  "_version": 2,
  "_seq_no": 1,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "num": 1
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 5.查询索引

GET /test_index/_search
1
{
  "took": 339,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "test_index",
        "_type": "_doc",
        "_id": "6",
        "_score": 1.0,
        "_source": {
          "num": 1
        }
      }
    ]
  }
}
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

# 6._version 字段

删除的时候是异步删除,只是做了删除标记,延时删除策略.

es 内部主从同步时,是多线程异步,乐观锁机制。也是基于版本号

  • 线程 1 先到,线程 2 后到,副本数据是没有问题的
  • 线程 2 先到,线程 1 后到:
    • 副本分片先把数据改为 test3,verion=3.
    • 线程 1 请求到了,副本分片,判断请求的 verison=1 太旧了,就会丢弃这个请求。

说明:

  • version 使用数据自带的 version 版本号

  • _version&version_type=external 则是并发时使用程序自己指定的 version,且是不存在的

PUT /test_index/_doc/4?version=2&version_type=external
{
  "test_field": "itcast1"
}
1
2
3
4

# 7.重试次数

指定重试次数

POST /test_index/_doc/5/_update?retry_on_conflict=3
{
  "doc": {
    "test_field": "itcast1"
  }
}
1
2
3
4
5
6

结合 version

POST /test_index/_doc/5/_update?retry_on_conflict=3&version=22&version_type=external
{
  "doc": {
    "test_field": "itcast1"
  }
}
1
2
3
4
5
6

# 8.批量查询 mget

单条查询 GET /test_index/1,如果查询多个 id 的文档一条一条查询,网络开销太大。

GET /_mget
{
   "docs" : [
      {
         "_index" : "test_index",
         "_type" :  "_doc",
         "_id" :    1
      },
      {
         "_index" : "test_index",
         "_type" :  "_doc",
         "_id" :    7
      }
   ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "docs": [
    {
      "_index": "test_index",
      "_type": "_doc",
      "_id": "2",
      "_version": 6,
      "_seq_no": 12,
      "_primary_term": 1,
      "found": true,
      "_source": {
        "test_field": "test12333123321321"
      }
    },
    {
      "_index": "test_index",
      "_type": "_doc",
      "_id": "3",
      "_version": 6,
      "_seq_no": 18,
      "_primary_term": 1,
      "found": true,
      "_source": {
        "test_field": "test3213"
      }
    }
  ]
}
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

提示去掉 type

GET /_mget
{
   "docs" : [
      {
         "_index" : "test_index",
         "_id" :    2
      },
      {
         "_index" : "test_index",
         "_id" :    3
      }
   ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

同一索引下批量查询:

GET /test_index/_mget
{
   "docs" : [
      {
         "_id" :    2
      },
      {
         "_id" :    3
      }
   ]
}
1
2
3
4
5
6
7
8
9
10
11

第三种写法:搜索写法

post /test_index/_doc/_search
{
    "query": {
        "ids" : {
            "values" : ["1", "7"]
        }
    }
}
1
2
3
4
5
6
7
8

# 9.bulk

Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

#语法
POST /_bulk
{"action": {"metadata"}}
{"data"}
1
2
3
4

示例:

#如下操作,删除5,新增14,修改2。
POST /_bulk
{ "create": { "_index": "test_index","_id": "8"}}
{ "test_field": "test8" }
{ "update": { "_index": "test_index","_id": "3"} }
{ "doc": {"test_field": "bulk test"} }
{ "delete": { "_index": "test_index","_id": "5" }}


1
2
3
4
5
6
7
8
9

总结:

  1. 功能:
    • delete:删除一个文档,只要 1 个 json 串就可以了
    • create:相当于强制创建 PUT /index/type/id/_create
    • index:普通的 put 操作,可以是创建文档,也可以是全量替换文档
    • update:执行的是局部更新 partial update 操作
  2. 格式:每个 json 不能换行。相邻 json 必须换行。
  3. 隔离:每个操作互不影响。操作失败的行会返回其失败信息。
  4. 实际用法:bulk 请求一次不要太大,否则一下积压到内存中,性能会下降。所以,一次请求几千个操作、大小在几 M 正好。

# 10.定位错误语法

验证错误语句:

GET /book/_validate/query?explain
{
  "query": {
    "mach": {
      "description": "java程序员"
    }
  }
}
1
2
3
4
5
6
7
8

返回:

{
  "valid": false,
  "error": "org.elasticsearch.common.ParsingException: no [query] registered for [mach]"
}
1
2
3
4

正确

GET /book/_validate/query?explain
{
  "query": {
    "match": {
      "description": "java程序员"
    }
  }
}
1
2
3
4
5
6
7
8

返回

{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "book",
      "valid": true,
      "explanation": "description:java description:程序员"
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

一般用在那种特别复杂庞大的搜索下,比如你一下子写了上百行的搜索,这个时候可以先用 validate api 去验证一下,搜索是否合法。

合法以后,explain 就像 mysql 的执行计划,可以看到搜索的目标等信息。

# 11.Scroll 分批查询

场景:下载某一个索引中 1 亿条数据,到文件或是数据库。

不能一下全查出来,系统内存溢出。所以使用 scoll 滚动搜索技术,一批一批查询。

scoll 搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的

每次发送 scroll 请求,我们还需要指定一个 scoll 参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了。

搜索

GET /book/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "size": 3
}
1
2
3
4
5
6
7

返回

{
  "_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAMOkWTURBNDUtcjZTVUdKMFp5cXloVElOQQ==",
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": []
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

获得的结果会有一个 scoll_id,下一次再发送 scoll 请求的时候,必须带上这个 scoll_id

GET /_search/scroll
{
    "scroll": "1m",
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAMOkWTURBNDUtcjZTVUdKMFp5cXloVElOQQ=="
}
1
2
3
4
5

与分页区别:

  • 分页是给用户看的 deep paging

  • scroll 是用户系统内部操作,如下载批量数据,数据转移。零停机改变索引映射。

# 12.deep paging

根据相关度评分倒排序,所以分页过深,协调节点会将大量数据聚合分析。

deep paging 性能问题:
1
  1. 消耗网络带宽,因为所搜过深的话,各 shard 要把数据传递给 coordinate node,这个过程是有大量数据传递的,消耗网络。
  2. 消耗内存,各 shard 要把数据传送给 coordinate node,这个传递回来的数据,是被 coordinate node 保存在内存中的,这样会大量消耗内存。
  3. 消耗 cup,coordinate node 要把传回来的数据进行排序,这个排序过程很消耗 cpu。

所以:鉴于 deep paging 的性能问题,所以应尽量减少使用。

# 13.fielddata

计算每个 tags 下的商品数量

设置字段"fielddata": true,这个步骤很重要,不然会报错

在 es 中,text 类型的字段使用一种叫做 fielddata 的查询时内存数据结构。当字段被排序,聚合或者通过脚本访问时这种数据结构会被创建。它是通过从磁盘读取每个段的整个反向索引来构建的,然后存存储在 java 的堆内存中。

fileddata 默认是不开启的。Fielddata 可能会消耗大量的堆空间,尤其是在加载高基数文本字段时。一旦 fielddata 已加载到堆中,它将在该段的生命周期内保留。此外,加载 fielddata 是一个昂贵的过程,可能会导致用户遇到延迟命中。这就是默认情况下禁用 fielddata 的原因。如果尝试对文本字段进行排序,聚合或脚本访问,将看到以下异常:

“Fielddata is disabled on text fields by default. Set fielddata=true on [your_field_name] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory.”

在启用 fielddata 之前,请考虑使用文本字段进行聚合,排序或脚本的原因。这样做通常没有意义。text 字段在索引例如 New York 这样的词会被分词,会被拆成 new,york。在此字段上面来一个 terms 的聚合会返回一个 new 的 bucket 和一个 york 的 bucket,当你想只返回一个 New York 的 bucket 的时候就会出现问题。

PUT /book/_mapping/
{
  "properties": {
    "tags": {
      "type": "text",
      "fielddata": true
    }
  }
}
1
2
3
4
5
6
7
8
9

# 14.minimum_should_match

minimum_should_match 参数用于设置返回的文档必须匹配的最少 should 分支数。参数值合法格式如下:

类型 示例 说明
正整数 3 无论有多少 should 分支,必须匹配指定数量的 should 分支。
负整数 -2 最多可以有多少个 should 分支不匹配。
正百分数 70% 必须匹配 should 分支总数的指定百分比数量的 should 分支,分支数向下取整。例如总共有 5 个 should 分支,则至少匹配 3 个。
负百分数 -20% 最多可以有 should 分支总数的指定百分比数量的 should 分支不匹配,分支数向下取整。例如总共有 7 个 should 分支,则最多有 1 个 should 分支不匹配。
组合 2<70% 一个正整数,然后是<,然后是上述的任意类型的说明符以此做为条件说明符。当 should 分支总数小于等于指定的数量时,则必须匹配所有 should 分支,当 should 分支总数大于指定的数量时,则应用指定的说明符。如示例中当 should 分支总数小于等于 2 时,则必须匹配所有分支,当 should 分支总数大于 2 时,则至少需要匹配 2 个 should 分支。
多组合 2<70% 6<-1 多个条件说明符用空格分隔,每个条件说明符只有在 should 分支数大于指定数量时有效。如示例中,当 should 分支总数小于等于 2 时,则必须全部匹配,当 should 分支总数大于 2 小于等于 6 时,则至少匹配 70%的 should 分支,当 should 分支总数大于 6 时,则最多有 1 个 should 分支不匹配。

如果基于说明符计算后不需要匹配 should 分支,那么布尔查询的通用规则:当布尔查询中没有 must 分支时必须匹配至少一个 should 分支,在检索时仍适用。无论计算的结果有多大或多小,需要匹配的 should 分支数永远不会小于 1 或大于 should 分支总数。

# 四.控制台实战

# 1.索引下所有数据

无条件搜索所有

GET /book/_search
1
{
  "took": 969,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "book",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "name": "Bootstrap开发",
          "description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
          "studymodel": "201002",
          "price": 38.6,
          "timestamp": "2019-08-25 19:11:35",
          "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
          "tags": ["bootstrap", "dev"]
        }
      }
      }
    ]
  }
}
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

# 2.带参数搜索

带参数查询

GET /db01_v1_20240914-091656_create_final_entities/_search?q=_id:Z1wb7pEB6ZYWVGEs_CO2
1

与 http 请求传参类似

GET /book/_search?q=name:java&sort=price:desc
1

类比 sql: select * from book where name like ’ %java%’ order by price desc

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      {
        "_index": "book",
        "_type": "_doc",
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "java编程思想",
          "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
          "studymodel": "201001",
          "price": 68.6,
          "timestamp": "2019-08-25 19:11:35",
          "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
          "tags": ["java", "dev"]
        },
        "sort": [68.6]
      }
    ]
  }
}
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

根据id搜索:

POST /db01_v1_20240914-091656_create_final_entities/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "_id": "Z1wb7pEB6ZYWVGEs_CO2"
          }
        }
      ]
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3. 设置 timeout

GET /book/_search?timeout=10ms
1

设置搜索的超时时间,到超时就返回

全局设置:配置文件中设置 search.default_search_timeout:100ms。默认不超时。

# 4.多索引搜索

multi-index 搜索模式

告诉你如何一次性搜索多个 index 和多个 type 下的数据

#所有索引下的所有数据都搜索出来
/_search

#指定一个index,搜索其下所有的数据
/index1/_search

#同时搜索两个index下的数据
/index1,index2/_search

#按照通配符去匹配多个索引
/index*/_search
1
2
3
4
5
6
7
8
9
10
11

# 5.分页搜索

分页搜索的语法:

select * from book limit 1,5
1
GET /book/_search?size=10

GET /book/_search?size=10&from=0

GET /book/_search?size=10&from=20

GET /book/_search?from=0&size=3
1
2
3
4
5
6
7

# 6.分页模糊搜索

# 分页模糊搜索
POST /db01_v1_20240903-114316/_search
{
  "from": 0,
  "size": 10,
  "query": {
    "query_string": {
      "query": "name:*模糊值*",
      "default_field": "name",
      "fuzziness": 1
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.query string 基础语法

  • GET /book/_search?q=name:java

  • GET /book/_search?q=+name:java

  • GET /book/_search?q=-name:java

说明:

q=field:search content 的语法,就是根据字段进行搜索

+与没有+一样都是必须包含,-是必须不包含

# 8._all metadata 的原理和作用

不带字段的查询,会存在 all 索引中_,方便查询

GET /book/_search?q=java
1

直接可以搜索所有的 field,任意一个 field 包含指定的关键字就可以搜索出来。我们在进行中搜索的时候,难道是对 document 中的每一个 field 都进行一次搜索吗?不是的。

es 中_all 元数据。建立索引的时候,插入一条 docunment,es 会将所有的 field 值经行全量分词,把这些分词,放到_all field 中。在搜索的时候,没有指定 field,就在_all 搜索。

# 9.query DSL

查询全部 GET /book/_search

GET /book/_search
{
  "query": { "match_all": {} }
}
1
2
3
4

排序 GET /book/_search?sort=price:desc

GET /book/_search
{
    "query" : {
        "match" : {
            "name" : " java"
        }
    },
    "sort": [
        { "price": "desc" }
    ]
}
1
2
3
4
5
6
7
8
9
10
11

分页查询 GET /book/_search?size=10&from=0

GET  /book/_search
{
  "query": { "match_all": {} },
  "from": 0,
  "size": 1
}
1
2
3
4
5
6

指定返回字段 GET /book/ _search? _source=name,studymodel

GET /book/_search
{
  "query": { "match_all": {} },
  "_source": ["name", "studymodel"]
}
1
2
3
4
5

通过组合以上各种类型查询,实现复杂查询。

# 10.组合多个搜索条件

搜索需求:title 必须包含 elasticsearch,content 可以包含 elasticsearch 也可以不包含,author_id 必须不为 111

sql where and or !=

初始数据:

POST /website/_doc/1
{
    "title": "my hadoop article",
    "content": "hadoop is very bad",
    "author_id": 111
}

POST /website/_doc/2
{
    "title": "my elasticsearch  article",
    "content": "es is very bad",
    "author_id": 112
}
POST /website/_doc/3
{
    "title": "my elasticsearch article",
    "content": "es is very goods",
    "author_id": 111
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

搜索:

GET /website/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "elasticsearch"
          }
        }
      ],
      "should": [
        {
          "match": {
            "content": "elasticsearch"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "author_id": 111
          }
        }
      ]
    }
  }
}
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

返回:

{
  "took": 488,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.47000363,
    "hits": [
      {
        "_index": "website",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.47000363,
        "_source": {
          "title": "my elasticsearch  article",
          "content": "es is very bad",
          "author_id": 112
        }
      }
    ]
  }
}
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

简单示例

POST /db01_v1_20240903/_search
{
  "from": 0,
  "size": 10,
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "object_id"
          }
        },
        {
          "match_phrase": {
            "object_id": "預約中醫門診服務"
          }
        }
      ]
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

更复杂的搜索需求:

select * from test_index where name='tom' or (hired =true and (personality ='good' and rude != true ))

GET /test_index/_search
{
    "query": {
            "bool": {
                "must": { "match":{ "name": "tom" }},
                "should": [
                    { "match":{ "hired": true }},
                    { "bool": {
                        "must":{ "match": { "personality": "good" }},
                        "must_not": { "match": { "rude": true }}
                    }}
                ],
                "minimum_should_match": 1
            }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 11.full-text search 全文检索

重新创建 book 索引

PUT /book/
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "description":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "studymodel":{
        "type": "keyword"
      },
      "price":{
        "type": "double"
      },
      "timestamp": {
         "type": "date",
         "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      },
      "pic":{
        "type":"text",
        "index":false
      }
    }
  }
}
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

插入数据

PUT /book/_doc/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "bootstrap", "dev"]
}

PUT /book/_doc/2
{
"name": "java编程思想",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "java", "dev"]
}

PUT /book/_doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2019-08-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "spring", "java"]
}
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

搜索

GET  /book/_search
{
    "query" : {
        "match" : {
            "description" : "java程序员"
        }
    }
}
1
2
3
4
5
6
7
8

# 12.Filter 过滤

需求:用户查询 description 中有"java 程序员",并且价格大于 80 小于 90 的数据。

GET /book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "description": "java程序员"
          }
        },
        {
          "range": {
            "price": {
              "gte": 80,
		      "lte": 90
            }
          }
        }
      ]
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

使用 filter:

GET /book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "description": "java程序员"
          }
        }
      ],
      "filter": {
        "range": {
          "price": {
            "gte": 80,
		     "lte": 90
          }
        }
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 13.filter 与 query 对比

filter,仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响。

query,会去计算每个 document 相对于搜索条件的相关度,并按照相关度进行排序。

应用场景:

一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用 query 如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用 filter

性能比较:

  • filter,不需要计算相关度分数,不需要按照相关度分数进行排序,同时还有内置的自动 cache 最常使用 filter 的数据

  • query,相反,要计算相关度分数,按照分数进行排序,而且无法 cache 结果

# 14.默认排序规则

默认情况下,是按照_score 降序排序的

然而,某些情况下,可能没有有用的_score,比如说 filter

GET book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "description": "java程序员"
          }
        }
      ]
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

当然,也可以是 constant_score

# 15.定制排序规则

相当于 sql 中 order by ?sort=sprice:desc

GET /book/_search
{
  "query": {
    "constant_score": {
      "filter" : {
            "term" : {
                "studymodel" : "201001"
            }
        }
    }
  },
  "sort": [
    {
      "price": {
        "order": "asc"
      }
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 16.Text 字段排序问题

如果对一个 text field 进行排序,结果往往不准确,因为分词后是多个单词,再排序就不是我们想要的结果了。

通常解决方案是,将一个 text field 建立两次索引,一个分词,用来进行搜索;一个不分词,用来进行排序。

PUT /website
{
  "mappings": {
  "properties": {
    "title": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword"
        }
      }
    },
    "content": {
      "type": "text"
    },
    "post_date": {
      "type": "date"
    },
    "author_id": {
      "type": "long"
    }
  }
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

插入数据

PUT /website/_doc/1
{
  "title": "first article",
  "content": "this is my second article",
  "post_date": "2019-01-01",
  "author_id": 110
}

PUT /website/_doc/2
{
    "title": "second article",
    "content": "this is my second article",
     "post_date": "2019-01-01",
    "author_id": 110
}

PUT /website/_doc/3
{
     "title": "third article",
     "content": "this is my third article",
     "post_date": "2019-01-02",
     "author_id": 110
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

搜索

GET /website/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "title.keyword": {
        "order": "desc"
      }
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 五.DSL 语法练习

DSL:Domain Specified Language,特定领域的语言

# 1.match_all

GET /book/_search
{
    "query": {
        "match_all": {}
    }
}
1
2
3
4
5
6

# 2.match

GET /book/_search
{
	"query": {
		"match": {
			"description": "java程序员"
		}
	}
}
1
2
3
4
5
6
7
8

# 3.multi_match

GET /book/_search
{
  "query": {
    "multi_match": {
      "query": "java程序员",
      "fields": ["name", "description"]
    }
  }
}
1
2
3
4
5
6
7
8
9

# 4.range query 范围查询

GET /book/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 80,
		"lte": 90
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11

# 5.term query

字段为 keyword 时,存储和搜索都不分词

GET /book/_search
{
  "query": {
    "term": {
      "description": "java程序员"
    }
  }
}
1
2
3
4
5
6
7
8

# 6.terms query

GET /book/_search
{
    "query": { "terms": { "tag": [ "search", "full_text", "nosql" ] }}
}

1
2
3
4
5

# 7.exist query

查询有某些字段值的文档

GET /_search
{
    "query": {
        "exists": {
            "field": "join_date"
        }
    }
}
1
2
3
4
5
6
7
8

# 8.Fuzzy query

返回包含与搜索词类似的词的文档,该词由 Levenshtein 编辑距离度量。

包括以下几种情况:

  • 更改角色(box→fox)

  • 删除字符(aple→apple)

  • 插入字符(sick→sic)

  • 调换两个相邻字符(ACT→CAT)

GET /book/_search
{
    "query": {
        "fuzzy": {
            "description": {
                "value": "jave"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10

# 9.IDs

GET /book/_search
{
    "query": {
        "ids" : {
            "values" : ["1", "4", "100"]
        }
    }
}
1
2
3
4
5
6
7
8

# 10.prefix 前缀查询

GET /book/_search
{
    "query": {
        "prefix": {
            "description": {
                "value": "spring"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10

# 11.regexp query

正则查询

GET /book/_search
{
    "query": {
        "regexp": {
            "description": {
                "value": "j.*a",
                "flags" : "ALL",
                "max_determinized_states": 10000,
                "rewrite": "constant_score"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 六.聚合 DSL API

# 1.all-分组-计数

计算每个 studymodel 下的商品数量

sql 语句: select studymodel,count(*) from book group by studymodel

GET /book/_search
{
  "size": 0,
  "query": {
    "match_all": {}
  },
  "aggs": {
    "group_by_model": {
      "terms": { "field": "studymodel" }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 2.分组-计数

计算每个 tags 下的商品数量

设置字段"fielddata": true,这个步骤很重要,不然会报错

在 es 中,text 类型的字段使用一种叫做 fielddata 的查询时内存数据结构。当字段被排序,聚合或者通过脚本访问时这种数据结构会被创建。它是通过从磁盘读取每个段的整个反向索引来构建的,然后存存储在 java 的堆内存中。

fileddata 默认是不开启的。Fielddata 可能会消耗大量的堆空间,尤其是在加载高基数文本字段时。一旦 fielddata 已加载到堆中,它将在该段的生命周期内保留。此外,加载 fielddata 是一个昂贵的过程,可能会导致用户遇到延迟命中。这就是默认情况下禁用 fielddata 的原因。如果尝试对文本字段进行排序,聚合或脚本访问,将看到以下异常:

“Fielddata is disabled on text fields by default. Set fielddata=true on [your_field_name] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory.”

在启用 fielddata 之前,请考虑使用文本字段进行聚合,排序或脚本的原因。这样做通常没有意义。text 字段在索引例如 New York 这样的词会被分词,会被拆成 new,york。在此字段上面来一个 terms 的聚合会返回一个 new 的 bucket 和一个 york 的 bucket,当你想只返回一个 New York 的 bucket 的时候就会出现问题。

PUT /book/_mapping/
{
  "properties": {
    "tags": {
      "type": "text",
      "fielddata": true
    }
  }
}
1
2
3
4
5
6
7
8
9

查询

GET /book/_search
{
  "size": 0,
  "query": {
    "match_all": {}
  },
  "aggs": {
    "group_by_tags": {
      "terms": { "field": "tags" }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3.搜索-分组

加上搜索条件,计算每个 tags 下的商品数量

GET /book/_search
{
  "size": 0,
  "query": {
    "match": {
      "description": "java程序员"
    }
  },
  "aggs": {
    "group_by_tags": {
      "terms": { "field": "tags" }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.分组-平均

先分组,再算每组的平均值,计算每个 tag 下的商品的平均价格

GET /book/_search
{
    "size": 0,
    "aggs" : {
        "group_by_tags" : {
            "terms" : {
              "field" : "tags"
            },
            "aggs" : {
                "avg_price" : {
                    "avg" : { "field" : "price" }
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 5.分组-平均-排序

计算每个 tag 下的商品的平均价格,并且按照平均价格降序排序

GET /book/_search
{
    "size": 0,
    "aggs" : {
        "group_by_tags" : {
            "terms" : {
              "field" : "tags",
              "order": {
                "avg_price": "desc"
              }
            },
            "aggs" : {
                "avg_price" : {
                    "avg" : { "field" : "price" }
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6.分组-分组-组内平均

按照指定的价格范围区间进行分组,然后在每组内再按照 tag 进行分组,最后再计算每组的平均价格

GET /book/_search
{
  "size": 0,
  "aggs": {
    "group_by_price": {
      "range": {
        "field": "price",
        "ranges": [
          {
            "from": 0,
            "to": 40
          },
          {
            "from": 40,
            "to": 60
          },
          {
            "from": 60,
            "to": 80
          }
        ]
      },
      "aggs": {
        "group_by_tags": {
          "terms": {
            "field": "tags"
          },
          "aggs": {
            "average_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}
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

# 七.聚合-电视案例

创建索引及映射

PUT /tvs
PUT /tvs/_search
{
			"properties": {
				"price": {
					"type": "long"
				},
				"color": {
					"type": "keyword"
				},
				"brand": {
					"type": "keyword"
				},
				"sold_date": {
					"type": "date"
				}
			}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

插入数据

POST /tvs/_bulk
{ "index": {}}
{ "price" : 1000, "color" : "红色", "brand" : "长虹", "sold_date" : "2019-10-28" }
{ "index": {}}
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2019-11-05" }
{ "index": {}}
{ "price" : 3000, "color" : "绿色", "brand" : "小米", "sold_date" : "2019-05-18" }
{ "index": {}}
{ "price" : 1500, "color" : "蓝色", "brand" : "TCL", "sold_date" : "2019-07-02" }
{ "index": {}}
{ "price" : 1200, "color" : "绿色", "brand" : "TCL", "sold_date" : "2019-08-19" }
{ "index": {}}
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2019-11-05" }
{ "index": {}}
{ "price" : 8000, "color" : "红色", "brand" : "三星", "sold_date" : "2020-01-01" }
{ "index": {}}
{ "price" : 2500, "color" : "蓝色", "brand" : "小米", "sold_date" : "2020-02-12" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.统计哪种颜色的电视销量最高

GET /tvs/_search
{
    "size" : 0,
    "aggs" : {
        "popular_colors" : {
            "terms" : {
              "field" : "color"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

查询条件解析

size:只获取聚合结果,而不要执行聚合的原始数据 aggs:固定语法,要对一份数据执行分组聚合操作 popular_colors:就是对每个 aggs,都要起一个名字, terms:根据字段的值进行分组 field:根据指定的字段的值进行分组

返回

{
  "took" : 18,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 8,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_colors" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "红色",
          "doc_count" : 4
        },
        {
          "key" : "绿色",
          "doc_count" : 2
        },
        {
          "key" : "蓝色",
          "doc_count" : 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

返回结果解析

hits.hits:我们指定了 size 是 0,所以 hits.hits 就是空的 aggregations:聚合结果 popular_color:我们指定的某个聚合的名称 buckets:根据我们指定的 field 划分出的 buckets key:每个 bucket 对应的那个值 doc_count:这个 bucket 分组内,有多少个数据 数量,其实就是这种颜色的销量

每种颜色对应的 bucket 中的数据的默认的排序规则:按照 doc_count 降序排序

# 2,统计每种颜色电视平均价格

GET /tvs/_search
{
   "size" : 0,
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": {
               "avg": {
                  "field": "price"
               }
            }
         }
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在一个 aggs 执行的 bucket 操作(terms),平级的 json 结构下,再加一个 aggs,这个第二个 aggs 内部,同样取个名字,执行一个 metric 操作,avg,对之前的每个 bucket 中的数据的指定的 field,price field,求一个平均值

返回:

{
  "took": 4,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 8,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "colors": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "红色",
          "doc_count": 4,
          "avg_price": {
            "value": 3250.0
          }
        },
        {
          "key": "绿色",
          "doc_count": 2,
          "avg_price": {
            "value": 2100.0
          }
        },
        {
          "key": "蓝色",
          "doc_count": 2,
          "avg_price": {
            "value": 2000.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

buckets,除了 key 和 doc_count avg_price:我们自己取的 metric aggs 的名字 value:我们的 metric 计算的结果,每个 bucket 中的数据的 price 字段求平均值后的结果

相当于 sql: select avg(price) from tvs group by color

# 3.继续下钻分析

每个颜色下,平均价格及每个颜色下,每个品牌的平均价格

GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "color_avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "group_by_brand": {
          "terms": {
            "field": "brand"
          },
          "aggs": {
            "brand_avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}
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

# 4.更多的 metric

count:bucket,terms,自动就会有一个 doc_count,就相当于是 count avg:avg aggs,求平均值 max:求一个 bucket 内,指定 field 值最大的那个数据 min:求一个 bucket 内,指定 field 值最小的那个数据 sum:求一个 bucket 内,指定 field 值的总和

GET /tvs/_search
{
   "size" : 0,
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": { "avg": { "field": "price" } },
            "min_price" : { "min": { "field": "price"} },
            "max_price" : { "max": { "field": "price"} },
            "sum_price" : { "sum": { "field": "price" } }
         }
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.划分范围 histogram

GET /tvs/_search
{
   "size" : 0,
   "aggs":{
      "price":{
         "histogram":{
            "field": "price",
            "interval": 2000
         },
         "aggs":{
            "income": {
               "sum": {
                 "field" : "price"
               }
             }
         }
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

histogram:类似于 terms,也是进行 bucket 分组操作,接收一个 field,按照这个 field 的值的各个范围区间,进行 bucket 分组操作

"histogram":{
  "field": "price",
  "interval": 2000
}
1
2
3
4

interval:2000,划分范围,0~2000,2000~4000,4000~6000,6000~8000,8000~10000,buckets

bucket 有了之后,一样的,去对每个 bucket 执行 avg,count,sum,max,min,等各种 metric 操作,聚合分析

# 6.按照日期分组聚合

date_histogram,按照我们指定的某个 date 类型的日期 field,以及日期 interval,按照一定的日期间隔,去划分 bucket

min_doc_count:即使某个日期 interval,2017-01-01~2017-01-31 中,一条数据都没有,那么这个区间也是要返回的,不然默认是会过滤掉这个区间的 extended_bounds,min,max:划分 bucket 的时候,会限定在这个起始日期,和截止日期内

GET /tvs/_search
{
   "size" : 0,
   "aggs": {
      "sales": {
         "date_histogram": {
            "field": "sold_date",
            "interval": "month",
            "format": "yyyy-MM-dd",
            "min_doc_count" : 0,
            "extended_bounds" : {
                "min" : "2019-01-01",
                "max" : "2020-12-31"
            }
         }
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 7.统计每季度每个品牌的销售额

GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_sold_date": {
      "date_histogram": {
        "field": "sold_date",
        "interval": "quarter",
        "format": "yyyy-MM-dd",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2019-01-01",
          "max": "2020-12-31"
        }
      },
      "aggs": {
        "group_by_brand": {
          "terms": {
            "field": "brand"
          },
          "aggs": {
            "sum_price": {
              "sum": {
                "field": "price"
              }
            }
          }
        },
        "total_sum_price": {
          "sum": {
            "field": "price"
          }
        }
      }
    }
  }
}
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

# 8.搜索与聚合结合,查询某个品牌按颜色销量

搜索与聚合可以结合起来。

sql select count(*)

from tvs

where brand like "%小米%"

group by color

es aggregation,scope,任何的聚合,都必须在搜索出来的结果数据中之行,搜索结果,就是聚合分析操作的 scope

GET /tvs/_search
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "小米"
      }
    }
  },
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 9.global bucket:单个品牌与所有品牌销量对比

aggregation,scope,一个聚合操作,必须在 query 的搜索结果范围内执行

出来两个结果,一个结果,是基于 query 搜索结果来聚合的; 一个结果,是对所有数据执行聚合的

GET /tvs/_search
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "小米"
      }
    }
  },
  "aggs": {
    "single_brand_avg_price": {
      "avg": {
        "field": "price"
      }
    },
    "all": {
      "global": {},
      "aggs": {
        "all_brand_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}
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

# 10.过滤+聚合:统计价格大于 1200 的电视平均价格

搜索+聚合

过滤+聚合

GET /tvs/_search
{
  "size": 0,
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "price": {
            "gte": 1200
          }
        }
      }
    }
  },
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 11.bucket filter:统计品牌最近一个月的平均价格

GET /tvs/_search
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "小米"
      }
    }
  },
  "aggs": {
    "recent_150d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-150d"
          }
        }
      },
      "aggs": {
        "recent_150d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    },
    "recent_140d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-140d"
          }
        }
      },
      "aggs": {
        "recent_140d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    },
    "recent_130d": {
      "filter": {
        "range": {
          "sold_date": {
            "gte": "now-130d"
          }
        }
      },
      "aggs": {
        "recent_130d_avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}
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

aggs.filter,针对的是聚合去做的

如果放 query 里面的 filter,是全局的,会对所有的数据都有影响

但是,如果,比如说,你要统计,长虹电视,最近 1 个月的平均值; 最近 3 个月的平均值; 最近 6 个月的平均值

bucket filter:对不同的 bucket 下的 aggs,进行 filter

# 12.按每种颜色的平均销售额降序排序

GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color",
        "order": {
          "avg_price": "asc"
        }
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

相当于 sql 子表数据字段可以立刻使用。

# 13.排序:按每种颜色的每种品牌平均销售额降序排序

GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "group_by_brand": {
          "terms": {
            "field": "brand",
            "order": {
              "avg_price": "desc"
            }
          },
          "aggs": {
            "avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}
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
上次更新: 10/29/2024, 10:27:50 AM