# 一.系统命令
# 1.获取基本信息
用于获取 Elasticsearch 集群的根节点信息。这个请求通常用于检查 Elasticsearch 服务是否正常运行,以及获取一些基本的状态信息。
get /
- 名称:集群的名称。
- 集群 UUID:集群的唯一标识符。
- 版本:Elasticsearch 服务的版本号。
- 标签:集群的标签信息,如果有的话。
- HTTP 地址:集群可以访问的 HTTP 地址列表。
- 构建:关于 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"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2._cluster 获取 health
GET /_cluster/health
解释: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
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3._cat 获取 health
GET /_cat/health?v
- 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/health
和 GET /_cat/health?v
都是用于获取 Elasticsearch 集群健康状态的 API,但它们有一些区别:
用途:
_cluster/health
:这是一个监控集群整体健康状态的 API,它可以提供集群级别的健康状态,包括索引的健康状态。它通常用于自动化脚本和应用程序中。_cat/health?v
:这是一个用于人类阅读的 API,它提供了易于阅读的表格格式输出,通常用于命令行或 Kibana 控制台中快速检查集群状态。
输出格式:
_cluster/health
:返回 JSON 格式的数据,适合程序解析和自动化处理。_cat/health?v
:返回纯文本格式的数据,包含列标题,更易于人类阅读。
功能:
_cluster/health
:除了提供集群健康状态,还可以等待直到集群达到期望的健康状态,并且可以指定返回分片级别的健康信息。_cat/health?v
:提供了集群健康状态的快照,包括节点总数、数据节点数、分片数等信息,但不提供等待功能。
参数:
_cluster/health
:支持多个参数,如wait_for_status
、timeout
、level
等,可以细化请求和控制返回的信息。_cat/health?v
:参数较少,v
参数用于显示列标题,ts
参数用于控制是否显示时间戳。
适用场景:
_cluster/health
:适合用于应用程序和自动化脚本中,需要详细和程序可操作的数据。_cat/health?v
:适合用于快速检查和监控集群状态,以及在命令行界面中进行故障排除。
# 5.查看索引
GET /_cat/indices?v
# 6.主键 id
手动 id
文档的 ID 被指定为 2
。
当这个请求被发送到 Elasticsearch 服务器后,服务器会处理这个请求,将提供的 JSON 数据存储在指定的索引和文档 ID 下。如果索引 book
不存在,Elasticsearch 会根据默认设置创建它。如果文档 ID 2
已经存在,则该文档将被更新;如果不存在,将创建一个新的文档。
PUT /book/2
{
"id":1,
"title":"这是一11文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
2
3
4
5
6
7
8
自动 id
POST /book/
{
"id":1,
"title":"这是一11文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
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
}
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
{ "acknowledged": true, "shards_acknowledged": true, "index": "demo_index" }
# 2.删除索引
DELETE /demo_index?pretty
DELETE db01_V1_20240911
2
{ "acknowledged": true }
# 3.先获取再删除
GET /_cat/indices?v
DELETE db01_v1_20240903-114316_
DELETE db01_v1_xingsen-poc-gpt-4o_
2
3
4
# 4.插入文档
put /blog_index/2
{
"title":"这是一篇文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
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
{ "_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
插入数据
PUT /book/1
{
"id":1,
"title":"这是一篇文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
2
3
4
5
6
7
8
查询数据
GET /book/1
使用 put 全量替换
实质:日文档的内容不会立即删除,只是标记为 deleted。适当的时机,集群会将这些文档删除。
PUT /book/1
{
"id":1,
"title":"这是一11文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
2
3
4
5
6
7
8
局部更新
- es 内部获取旧文档
- 将传来的文档 field 更新到旧数据(内存)
- 将旧文档标记问 delete
- 创建新文档
POST /book/_doc/1/_update
{
"doc": {
"title": "这是一333文章"
}
}
2
3
4
5
6
noop:
- 多次执行"result" : "noop’
- no operation
局部更新
POST /book/_update/1
{
"doc": {
"title": "这是一3333333444555文章"
}
}
2
3
4
5
6
删除数据
DELETE /book/_doc/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"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2._source 字段
含义:插入数据时的所有字段和值。在 get 获取数据时,在 source 字段中原样返回。
GET /book/_doc/1
定制返回:
GET /book/_doc/1?_source_includes=id,title
{ "_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
PUT /read_index/_doc/1/_create
{
"id":1,
"title":"这是一11文章",
"content":"xxxxx",
"comment":"备注信息",
"mobile":"13344556677"
}
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
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
{ "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"
}
2
3
4
# 7.重试次数
指定重试次数
POST /test_index/_doc/5/_update?retry_on_conflict=3
{
"doc": {
"test_field": "itcast1"
}
}
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"
}
}
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
}
]
}
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
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
同一索引下批量查询:
GET /test_index/_mget
{
"docs" : [
{
"_id" : 2
},
{
"_id" : 3
}
]
}
2
3
4
5
6
7
8
9
10
11
第三种写法:搜索写法
post /test_index/_doc/_search
{
"query": {
"ids" : {
"values" : ["1", "7"]
}
}
}
2
3
4
5
6
7
8
# 9.bulk
Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。
#语法
POST /_bulk
{"action": {"metadata"}}
{"data"}
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" }}
2
3
4
5
6
7
8
9
总结:
- 功能:
- delete:删除一个文档,只要 1 个 json 串就可以了
- create:相当于强制创建 PUT /index/type/id/_create
- index:普通的 put 操作,可以是创建文档,也可以是全量替换文档
- update:执行的是局部更新 partial update 操作
- 格式:每个 json 不能换行。相邻 json 必须换行。
- 隔离:每个操作互不影响。操作失败的行会返回其失败信息。
- 实际用法:bulk 请求一次不要太大,否则一下积压到内存中,性能会下降。所以,一次请求几千个操作、大小在几 M 正好。
# 10.定位错误语法
验证错误语句:
GET /book/_validate/query?explain
{
"query": {
"mach": {
"description": "java程序员"
}
}
}
2
3
4
5
6
7
8
返回:
{
"valid": false,
"error": "org.elasticsearch.common.ParsingException: no [query] registered for [mach]"
}
2
3
4
正确
GET /book/_validate/query?explain
{
"query": {
"match": {
"description": "java程序员"
}
}
}
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:程序员"
}
]
}
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
}
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": []
}
}
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=="
}
2
3
4
5
与分页区别:
分页是给用户看的 deep paging
scroll 是用户系统内部操作,如下载批量数据,数据转移。零停机改变索引映射。
# 12.deep paging
根据相关度评分倒排序,所以分页过深,协调节点会将大量数据聚合分析。
deep paging 性能问题:
- 消耗网络带宽,因为所搜过深的话,各 shard 要把数据传递给 coordinate node,这个过程是有大量数据传递的,消耗网络。
- 消耗内存,各 shard 要把数据传送给 coordinate node,这个传递回来的数据,是被 coordinate node 保存在内存中的,这样会大量消耗内存。
- 消耗 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
}
}
}
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
{ "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
与 http 请求传参类似
GET /book/_search?q=name:java&sort=price:desc
类比 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"
}
}
]
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 设置 timeout
GET /book/_search?timeout=10ms
设置搜索的超时时间,到超时就返回
全局设置:配置文件中设置 search.default_search_timeout:100ms。默认不超时。
# 4.多索引搜索
multi-index 搜索模式
告诉你如何一次性搜索多个 index 和多个 type 下的数据
#所有索引下的所有数据都搜索出来
/_search
#指定一个index,搜索其下所有的数据
/index1/_search
#同时搜索两个index下的数据
/index1,index2/_search
#按照通配符去匹配多个索引
/index*/_search
2
3
4
5
6
7
8
9
10
11
# 5.分页搜索
分页搜索的语法:
select * from book limit 1,5
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
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
}
}
}
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
直接可以搜索所有的 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": {} }
}
2
3
4
排序 GET /book/_search?sort=price:desc
GET /book/_search
{
"query" : {
"match" : {
"name" : " java"
}
},
"sort": [
{ "price": "desc" }
]
}
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
}
2
3
4
5
6
指定返回字段 GET /book/ _search? _source=name,studymodel
GET /book/_search
{
"query": { "match_all": {} },
"_source": ["name", "studymodel"]
}
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
}
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
}
}
]
}
}
}
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
}
}
]
}
}
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": "預約中醫門診服務"
}
}
]
}
}
}
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
}
}
}
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
}
}
}
}
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"]
}
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程序员"
}
}
}
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
}
}
}
]
}
}
}
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
}
}
}
}
}
}
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程序员"
}
}
]
}
}
}
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"
}
}
]
}
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"
}
}
}
}
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
}
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"
}
}
]
}
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": {}
}
}
2
3
4
5
6
# 2.match
GET /book/_search
{
"query": {
"match": {
"description": "java程序员"
}
}
}
2
3
4
5
6
7
8
# 3.multi_match
GET /book/_search
{
"query": {
"multi_match": {
"query": "java程序员",
"fields": ["name", "description"]
}
}
}
2
3
4
5
6
7
8
9
# 4.range query 范围查询
GET /book/_search
{
"query": {
"range": {
"price": {
"gte": 80,
"lte": 90
}
}
}
}
2
3
4
5
6
7
8
9
10
11
# 5.term query
字段为 keyword 时,存储和搜索都不分词
GET /book/_search
{
"query": {
"term": {
"description": "java程序员"
}
}
}
2
3
4
5
6
7
8
# 6.terms query
GET /book/_search
{
"query": { "terms": { "tag": [ "search", "full_text", "nosql" ] }}
}
2
3
4
5
# 7.exist query
查询有某些字段值的文档
GET /_search
{
"query": {
"exists": {
"field": "join_date"
}
}
}
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"
}
}
}
}
2
3
4
5
6
7
8
9
10
# 9.IDs
GET /book/_search
{
"query": {
"ids" : {
"values" : ["1", "4", "100"]
}
}
}
2
3
4
5
6
7
8
# 10.prefix 前缀查询
GET /book/_search
{
"query": {
"prefix": {
"description": {
"value": "spring"
}
}
}
}
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"
}
}
}
}
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" }
}
}
}
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
}
}
}
2
3
4
5
6
7
8
9
查询
GET /book/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"group_by_tags": {
"terms": { "field": "tags" }
}
}
}
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" }
}
}
}
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" }
}
}
}
}
}
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" }
}
}
}
}
}
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"
}
}
}
}
}
}
}
}
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"
}
}
}
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" }
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"
}
}
}
}
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
}
]
}
}
}
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"
}
}
}
}
}
}
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
}
}
]
}
}
}
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"
}
}
}
}
}
}
}
}
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" } }
}
}
}
}
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"
}
}
}
}
}
}
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
}
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"
}
}
}
}
}
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"
}
}
}
}
}
}
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"
}
}
}
}
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"
}
}
}
}
}
}
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"
}
}
}
}
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"
}
}
}
}
}
}
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"
}
}
}
}
}
}
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"
}
}
}
}
}
}
}
}
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