由于笔者在实际项目仅仅将ES用作索引数据库,并没有深入研究过ES的搜索功能。而且鉴于笔者的搜索引擎知识有限,本文将仅仅介绍ES简单(非全文)的查询API。

笔者原本打算在本文中介绍聚合API的内容,但是写着写着发现文章有点过长,不便于阅读,故将聚合API的内容移至下一篇博客中。

引言

单单介绍理论和API是乏味和低效率的,本文将结合一个实际的例子来介绍这些API。下表是本文数据表的表结构,表名(type)为“student”。注意,studentNo是本表的id,也就是_id字段的值与studentNo的值保持一致。

字段名 字段含义 类型 是否能被索引 备注
studentNo 学号 string id
name 姓名 string  
sex 性别 string  
age 年龄 integer  
birthday 出生年月 date  
address 家庭住址 string  
classNo 班级 string  
isLeader 是否为班干部 boolean  

上面的表结构所对应的mapping如下,将数据保存在索引名为“student”的索引中。

{
"student": {
"properties": {
"studentNo": {
"type": "string",
"index": "not_analyzed"
},
"name": {
"type": "string",
"index": "not_analyzed"
},
"male": {
"type": "string",
"index": "not_analyzed"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd"
},
"address": {
"type": "string",
"index": "not_analyzed"
},
"classNo": {
"type": "string",
"index": "not_analyzed "
},
"isLeader": {
"type": "boolean"
}
}
}
}

索引中保存的数据如下,下面介绍的所有API都将基于这个数据表。

studentNo name male age birthday classNo address isLeader
1 刘备 24 1985-02-03 1 湖南省长沙市 true
2 关羽 22 1987-08-23 2 四川省成都市 false
3 糜夫人 19 1990-06-12 1 上海市 false
4 张飞 20 1989-07-30 3 北京市 false
5 诸葛亮 18 1992-04-27 2 江苏省南京市 true
6 孙尚香 16 1994-05-21 3   false
7 马超 19 1991-10-20 1 黑龙江省哈尔滨市 false
8 赵云 23 1986-10-26 2 浙江省杭州市 false

查询API

ES中的查询非常灵活,为用户提供了非常方便而强大的API。个人觉得ES的调用接口设计得非常好,所有接口合理且风格一致,值得好好研究!

Query和Filter

ES为用户提供两类查询API,一类是在查询阶段就进行条件过滤的query查询,另一类是在query查询出来的数据基础上再进行过滤的filter查询。这两类查询的区别是:

  • query方法会计算查询条件与待查询数据之间的相关性,计算结果写入一个score字段,类似于搜索引擎。filter仅仅做字符串匹配,不会计算相关性,类似于一般的数据查询,所以filter得查询速度比query快。
  • filter查询出来的数据会自动被缓存,而query不能。

query和filter可以单独使用,也可以相互嵌套使用,非常灵活。

Query查询

下面的情况下适合使用query查询:

  • 需要进行全文搜索。
  • 查询结果依赖于相关性,即需要计算查询串和数据的相关性。

(1)Match All Query

查询所有的数据,相当于不带条件查询。下面的代码是一个典型的match_all查询的调用方式。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
}
}
'

查询结果如下。其他所有的查询都是返回这种格式的数据。

{
"took": 156, // 查询耗时(毫秒)
"timed_out": false, // 是否超时
"_shards": {
"total": 5, // 总共查询的分片数
"successful": 5, // 查询成功的分片数
"failed": 0 // 查询失败的分片数
},
"hits": {
"total": 8, // 本次查询的记录数
"max_score": 1, // 查询所有数据中的最大score
"hits": [ // 数据列表
{
"_index": "student", // 数据所属的索引名
"_type": "student", // 数据所属的type
"_id": "4", // 数据的id值
"_score": 1, // 该记录的score
"_source": { // ES将原始数据保存到_source字段中
"studentNo": "4",
"name": "张飞",
"male": "男",
"age": "20",
"birthday": "1989-07-30",
"classNo": "3",
"isLeader": "F"
}
},
{
…… // 其他的数据格式相同,就不列出来了
}
]
}
}

查询时,你会发现无论数据量有多大,每次最多只能查到10条数据。这是因为ES服务端默认对查询结果做了分页处理,每页默认的大小为10。如果想自己指定查询的数据,可使用from和size字段,并且按指定的字段排序。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
},
"from": 2, // 从2条记录开始取
"size": 4, // 取4条数据
"sort": {
"studentNo": { // 按studentNo字段升序
"order": "asc"// 降序为desc
}
}
}
'

注意:不要把from设得过大(超过10000),否则会导致ES服务端因频繁GC而无法正常提供服务。其实实际项目中也没有谁会翻那么多页,但是为了ES的可用性,务必要对分页查询的页码做一定的限制。

(2)term query

词语查询,如果是对未分词的字段进行查询,则表示精确查询。查找名为“诸葛亮”的学生,查询结果为学号为5的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"term": {
"name": "诸葛亮"
}
}
}
'

(3)Bool Query

Bool(布尔)查询是一种复合型查询,它可以结合多个其他的查询条件。主要有3类逻辑查询:

  • must:查询结果必须符合该查询条件(列表)。
  • should:类似于in的查询条件。如果bool查询中不包含must查询,那么should默认表示必须符合查询列表中的一个或多个查询条件。
  • must_not:查询结果必须不符合查询条件(列表)。

查找2班的班干部,查询结果为学号为5的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'

(4)Ids Query

id字段查询。查询数据id值为1和2的同学,由于id的值与studentNo相同,故查询结果为学号为1和2的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"ids": {
"type": "student",
"values": [
"1",
"2"
]
}
}
}
'

(5)Prefix Query

前缀查询。查找姓【赵】的同学,查询结果是学号为8的赵云。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"prefix": {
"name": "赵"
}
}
}
'

(6)Range Query

范围查询,针对date和number类型的数据。查找年龄到18~20岁的同学,查询结果是学号为3、4、5、7的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"range": {
"age": {
"gte": "18", // 表示>=
"lte": "20" // 表示<=
}
}
}
}
'

实际上,对于date类型的数据,ES中以其时间戳(长整形)的形式存放的。

(7)Terms Query

多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询。下面查找学号为1,3的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
'

(8)Wildcard Query

通配符查询,是简化的正则表达式查询,包括下面两类通配符:

  • * 代表任意(包括0个)多个字符
  • ? 代表任意一个字符

查找名字的最后一个字是“亮”的同学,查询结果是学号为5的诸葛亮。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"wildcard": {
"name": "*亮"
}
}
}
'

(9)Regexp Query同学

正则表达式查询,这是最灵活的字符串类型字段查询方式。查找家住长沙市的学生,查询结果为学号为1的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"regexp": {
"address": ".*长沙市.*" // 这里的.号表示任意一个字符
}
}
}
'

Filter查询

下面的情况下适合使用filter查询:

  • yes/no的二元查询
  • 针对精确值进行查询

filter和query的查询方式有不少是重叠的,所以本节仅仅介绍API的调用,一些通用的注意的事项就不再重复了。

(1)Term Filter

词语查询,如果是对未分词的字段进行查询,则表示精确查询。查找名为“诸葛亮”的学生,查询结果为学号为5的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"term": {
"name": "诸葛亮",
"_cache" : true // 与query主要是这里的区别,可以设置数据缓存
}
}
}
'

filter查询方式都可以通过设置_cache为true来缓存数据。如果下一次恰好以相同的查询条件进行查询并且该缓存没有过期,就可以直接从缓存中读取数据,这样就大大加快的查询速度。

(2)Bool Filter

查找2班的班干部,查询结果为学号为5的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'

(3)And Filter

And逻辑连接查询,连接1个或1个以上查询条件。它与bool查询中的must查询非常相似。实际上,and查询可以转化为对应的bool查询。查找2班的班干部,查询结果为学号为5的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"and": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'

(4)Or Filter

Or连接查询,表示逻辑或。。查找2班或者是班干部的学生名单,查询结果为学号为1、2、5、8的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"or": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'

(5)Exists Filter

存在查询,查询指定字段至少包含一个非null值的数据。如果字段索引为not_analyzed类型,则查询sql中的is not null查询方式。查询地址存在学生,查询结果为除了6之外的所有学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"exists": {
"field": "address"
}
}
}
'

(6)Missing Filter

缺失值查询,与Exists查询正好相反。查询地址不存在的学生,查询结果为学号为6的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"missing": {
"field": "address"
}
}
}
'

(7)Prefix Filter

前缀查询。查找姓【赵】的同学,查询结果是学号为8的赵云。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"prefix": {
"name": "赵"
}
}
}
'

(8)Range Filter

范围查询,针对date和number类型的数据。查找年龄到18~20岁的同学,查询结果是学号为3、4、5、7的记录。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"range": {
"age": {
"gte": "18",
"lte": "20"
}
}
}
}
'

(9)Terms Filter

多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询。下面查找学号为1,3的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
'

(10)Regexp Filter

正则表达式查询,是最灵活的字符串类型字段查询方式。查找家住长沙市的学生,查询结果为学号为1的学生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"regexp": {
"address": ".*长沙市.*"
}
}
}
'

Aggregations (聚合)API的使用

 

ES提供的聚合功能可以用来进行简单的数据分析。本文仍然以上一篇提供的数据为例来讲解。数据如下:

studentNo name male age birthday classNo address isLeader
1 刘备 24 1985-02-03 1 湖南省长沙市 true
2 关羽 22 1987-08-23 2 四川省成都市 false
3 糜夫人 19 1990-06-12 1 上海市 false
4 张飞 20 1989-07-30 3 北京市 false
5 诸葛亮 18 1992-04-27 2 江苏省南京市 true
6 孙尚香 16 1994-05-21 3   false
7 马超 19 1991-10-20 1 黑龙江省哈尔滨市 false
8 赵云 23 1986-10-26 2 浙江省杭州市 false

本文的主要内容有:

  1. metric API的使用
  2. bucketing API的使用
  3. 两类API的嵌套使用

1. 聚合API

ES中的Aggregations API是从Facets功能基础上发展而来,官网正在进行替换计划,建议用户使用Aggregations API,而不是Facets API。ES中的聚合上可以分为下面两类:

  1. metric(度量)聚合:度量类型聚合主要针对的number类型的数据,需要ES做比较多的计算工作
  2. bucketing(桶)聚合:划分不同的“桶”,将数据分配到不同的“桶”里。非常类似sql中的group语句的含义。

metric既可以作用在整个数据集上,也可以作为bucketing的子聚合作用在每一个“桶”中的数据集上。当然,我们可以把整个数据集合看做一个大“桶”,所有的数据都分配到这个大“桶”中。

ES中的聚合API的调用格式如下:

"aggregations" : {                  // 表示聚合操作,可以使用aggs替代
"<aggregation_name>" : { // 聚合名,可以是任意的字符串。用做响应的key,便于快速取得正确的响应数据。
"<aggregation_type>" : { // 聚合类别,就是各种类型的聚合,如min等
<aggregation_body> // 聚合体,不同的聚合有不同的body
}
[,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套的子聚合,可以有0或多个
}
[,"<aggregation_name_2>" : { ... } ]* // 另外的聚合,可以有0或多个
}

1.1 度量类型(metric)聚合

(1)Min Aggregation

最小值查询,作用于number类型字段上。查询2班最小的年龄值。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": { // 可以先使用query查询得到需要的数据集
"term": {
"classNo": "2"
}
},
"aggs": {
"min_age": {
"min": {
"field": "age"
}
}
}
}
'

查询结果为:

{
"took": 19, // 前面部分数据与普通的查询数据相同
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.4054651,
"hits": [
{
"_index": "student",
"_type": "student",
"_id": "2",
"_score": 1.4054651,
"_source": {
"studentNo": "2",
"name": "关羽",
"male": "男",
"age": "22",
"birthday": "1987-08-23",
"classNo": "2",
"isLeader": "false"
}
},
{
"_index": "student",
"_type": "student",
"_id": "8",
"_score": 1,
"_source": {
"studentNo": "8",
"name": "赵云",
"male": "男",
"age": "23",
"birthday": "1986-10-26",
"classNo": "2",
"isLeader": "false"
}
},
{
"_index": "student",
"_type": "student",
"_id": "5",
"_score": 0.30685282,
"_source": {
"studentNo": "5",
"name": "诸葛亮",
"male": "男",
"age": "18",
"birthday": "1992-04-27",
"classNo": "2",
"isLeader": "true"
}
}
]
},
"aggregations": { // 聚合结果
"min_age": { // 前面输入的聚合名
"value": 18, // 聚合后的数据
"value_as_string": "18.0"
}
}
}

上面的聚合查询有两个要注意的点:

  1. 可以通过query先过滤数据
  2. 返回的结果会包含聚合操作所作用的数据全集

有时候我们对作用的数据全集并不太敢兴趣,我们仅仅需要最终的聚合结果。可以通过查询类型(search_type)参数来实现这个需求。下面查询出来的数据量会大大减少,ES内部也会在查询时减少一些耗时的步骤,所以查询效率会提高。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d     // 注意这里的search_type=count
'
{
"query": { // 可以先使用query查询得到需要的数据集
"term": {
"classNo": "2"
}
},
"aggs": {
"min_age": {
"min": {
"field": "age"
}
}
}
}
'

本次的查询结果为:

{
... "aggregations": { // 聚合结果
"min_age": { // 前面输入的聚合名
"value": 18, // 聚合后的数据
"value_as_string": "18.0"
}
}
}

(2)Max Aggregation

最大值查询。下面查询2班最大的年龄值,查询结果为23。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"query": {
"term": {
"classNo": "2"
}
},
"aggs": {
"max_age": {
"max": {
"field": "age"
}
}
}
}
'

(3)Sum Aggregation

数值求和。下面统计查询2班的年龄总和,查询结果为63。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"query": {
"term": {
"classNo": "2"
}
},
"aggs": {
"sum_age": {
"sum": {
"field": "age"
}
}
}
}
'

(4)Avg Aggregation

计算平均值。下面计算查询2班的年龄平均值,结果为21。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"query": {
"term": {
"classNo": "2"
}
},
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
}
}
}
'

(5)Stats Aggregation

统计查询,一次性统计出某个字段上的常用统计值。下面对整个学校的学生进行简单地统计。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"stats_age": {
"stats": {
"field": "age"
}
}
}
}
'

查询结果为:

{
... // 次要数据省略 "aggregations": {
"stats_age": {
"count": 8, // 含有年龄数据的学生计数
"min": 16, // 年龄最小值
"max": 24, // 年龄最大值
"avg": 20.125, // 年龄平均值
"sum": 161, // 年龄总和
"min_as_string": "16.0",
"max_as_string": "24.0",
"avg_as_string": "20.125",
"sum_as_string": "161.0"
}
}
}

(6)Top hits Aggregation

取符合条件的前n条数据记录。下面查询全校年龄排在前2位的学生,仅需返回学生姓名和年龄。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
{
"aggs": {
"top_age": {
"top_hits": {
"sort": [ // 排序
{
"age": { // 按年龄降序
"order": "desc"
}
}
],
"_source": {
"include": [ // 指定返回字段
"name",
"age"
]
},
"size": 2 // 取前2条数据
}
}
}
}

返回结果为:

{
... "aggregations": {
"top_age": {
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "student",
"_type": "student",
"_id": "1",
"_score": null,
"_source": {
"name": "刘备",
"age": "24"
},
"sort": [
24
]
},
{
"_index": "student",
"_type": "student",
"_id": "8",
"_score": null,
"_source": {
"name": "赵云",
"age": "23"
},
"sort": [
23
]
}
]
}
}
}
}

1.2 桶类型(bucketing)聚合

(1)Terms Aggregation

按照指定的1或多个字段将数据划分成若干个小的区间,计算落在每一个区间上记录数量,并按指定顺序进行排序。下面统计每个班的学生数,并按学生数从大到小排序,取学生数靠前的2个班级。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"terms_classNo": {
"terms": {
"field": "classNo", // 按照班号进行分组
"order": { // 按学生数从大到小排序
"_count": "desc"
},
"size": 2 // 取前两名
}
}
}
}
'

值得注意的,取得的前2名的学生数实际上是一个近似值,ES的实现方式参见这里。如果想要取得精确值,可以不指定size值,使其进行一次全排序,然后在程序中自行去取前2条记录。当然,这样做会使得ES做大量的排序运算工作,效率比较差。

(2)Range Aggregation

自定义区间范围的聚合,我们可以自己手动地划分区间,ES会根据划分出来的区间将数据分配不同的区间上去。下面将全校学生按照年龄划分为5个区间段:16岁以下、16~18、19~21、22~24、24岁以上,要求统计每一个年龄段内的学生数。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"range_age": {
"range": {
"field": "age",
"ranges": [
{
"to": 15
},
{
"from": "16",
"to": "18"
},
{
"from": "19",
"to": "21"
}, {
"from": "22",
"to": "24"
},
{
"from": "25"
}
]
}
}
}
}
'

(3)Date Range Aggregation

时间区间聚合专门针对date类型的字段,它与Range Aggregation的主要区别是其可以使用时间运算表达式。主要包括+(加法)运算、-(减法)运算和/(四舍五入)运算,每种运算都可以作用在不同的时间域上面,下面是一些时间运算表达式示例。

  • now+10y:表示从现在开始的第10年。
  • now+10M:表示从现在开始的第10个月。
  • 1990-01-10||+20y:表示从1990-01-01开始后的第20年,即2010-01-01。
  • now/y:表示在年位上做舍入运算。今天是2015-09-06,则这个表达式计算结果为:2015-01-01。说好的rounding运算呢?结果是做的flooring运算,不知道为啥,估计是我理解错了-_-!!

下面查询25年前及更早出生的学生数。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"range_age": {
"date_range": {
"field": "birthday",
"ranges": [
{
"to": "now-25y"
}
]
}
}
}
}
'

(4)Histogram Aggregation

直方图聚合,它将某个number类型字段等分成n份,统计落在每一个区间内的记录数。它与前面介绍的Range聚合非常像,只不过Range可以任意划分区间,而Histogram做等间距划分。既然是等间距划分,那么参数里面必然有距离参数,就是interval参数。下面按学生年龄统计各个年龄段内的学生数量,分隔距离为2岁。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"histogram_age": {
"histogram": {
"field": "age",
"interval": 2, // 距离为2
"min_doc_count": 1 // 只返回记录数量大于等于1的区间
}
}
}
}
'

(5)Date Histogram Aggregation

时间直方图聚合,专门对时间类型的字段做直方图聚合。这种需求是比较常用见得的,我们在统计时,通常就会按照固定的时间断(1个月或1年等)来做统计。下面统计学校中同一年出生的学生数。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"data_histogram_birthday": {
"date_histogram": {
"field": "birthday",
"interval": "year", // 按年统计
"format": "yyyy" // 返回结果的key的格式
}
}
}
}
'

返回结果如下,可以看到由于上面的”format”: “yyyy”,所以返回的key_as_string只返回年的信息。

{
"buckets": [
{
"key_as_string": "1985",
"key": 473385600000,
"doc_count": 1
},
{
"key_as_string": "1986",
"key": 504921600000,
"doc_count": 1
},
{
"key_as_string": "1987",
"key": 536457600000,
"doc_count": 1
},
{
"key_as_string": "1989",
"key": 599616000000,
"doc_count": 1
},
{
"key_as_string": "1990",
"key": 631152000000,
"doc_count": 1
},
{
"key_as_string": "1991",
"key": 662688000000,
"doc_count": 1
},
{
"key_as_string": "1992",
"key": 694224000000,
"doc_count": 1
},
{
"key_as_string": "1994",
"key": 757382400000,
"doc_count": 1
}
]
}

(6)Missing Aggregation

值缺损聚合,它是一类单桶聚合,也就是最终只会产生一个“桶”。下面统计学生信息中地址栏缺损的记录数量。由于只有学号为6的孙尚香的地址缺损,所以统计值为1。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"missing_address": {
"missing": {
"field": "address"
}
}
}
}
'

1.3 嵌套使用

前面已经说过,聚合操作是可以嵌套使用的。通过嵌套,可以使得metric类型的聚合操作作用在每一“桶”上。我们可以使用ES的嵌套聚合操作来完成稍微复杂一点的统计功能。下面统计每一个班里最大的年龄值。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
"aggs": {
"missing_address": {
"terms": {
"field": "classNo"
},
"aggs": { // 在这里嵌套新的子聚合
"max_age": {
"max": { // 使用max聚合
"field": "age"
}
}
}
}
}
}
'

返回结果如下:

{
"buckets": [
{
"key": "1", // key是班级号
"doc_count": 3, // 每个班级内的人数
"max_age": { // 这里是我们指定的子聚合名
"value": 24, // 每班的年龄值
"value_as_string": "24.0"
}
},
{
"key": "2",
"doc_count": 3,
"max_age": {
"value": 23,
"value_as_string": "23.0"
}
},
{
"key": "3",
"doc_count": 1,
"max_age": {
"value": 20,
"value_as_string": "20.0"
}
},
{
"key": "4",
"doc_count": 1,
"max_age": {
"value": 16,
"value_as_string": "16.0"
}
}
]
}

2. 总结

本文介绍了ES中的一些常用的聚合API的使用,包括metric、bucketing以及它们的嵌套使用方法。掌握了这些API就可以完成简单的数据统计功能,更多的API详见官方文档

 
想进阶的同学,请看:ES权威指南

ES的Query、Filter、Metric、Bucketing使用详解的更多相关文章

  1. ES系列七、ES-倒排索引详解

    1.单词——文档矩阵 单词-文档矩阵是表达两者之间所具有的一种包含关系的概念模型,图3-1展示了其含义.图3-1的每列代表一个文档,每行代表一个单词,打对勾的位置代表包含关系. 图3-1 单词-文档矩 ...

  2. 基于jquery的has()方法以及与find()方法以及filter()方法的区别详解

    has(selector选择器或DOM元素)   将匹配元素集合根据选择器或DOM元素为条件,检索该条件在每个元素的后代中是否存在,将符合条件的的元素构成新的结果集. 下面举一个例子: <ul& ...

  3. js数组中indexOf/filter/forEach/map/reduce详解

    今天在网上看到一篇帖子,如题: 出处:前端开发博客 (http://caibaojian.com/5-array-methods.html) 在ES5中一共有9个Array方法,分别是: Array. ...

  4. JS Array常用方法indexOf/filter/forEach/map/reduce详解

    Array共有九个方法   Array.prototype.indexOf Array.prototype.lastIndexOf Array.prototype.every Array.protot ...

  5. Query Profiler 和Explain 用法详解

    一.Query Profiler  MySQL 的Query Profiler 是一个使用非常方便的Query 诊断分析工具,通过该工具可以获取一条Query 在整个执行过程中多种资源的消耗情况,如C ...

  6. SpringBoot初始教程之Servlet、Filter、Listener配置详解

    1.介绍 通过之前的文章来看,SpringBoot涵盖了很多配置,但是往往一些配置是采用原生的Servlet进行的,但是在SpringBoot中不需要配置web.xml的 因为有可能打包之后是一个ja ...

  7. Filter及FilterChain的详解

    一.Filter的介绍及使用 什么是过滤器? 与Servlet相似,过滤器是一些web应用程序组件,可以绑定到一个web应用程序中.但是与其他web应用程序组件不同的是,过滤器是"链&quo ...

  8. 5个现在就该使用的数组Array方法: indexOf/filter/forEach/map/reduce详解(转)

    ECMAScript5标准发布于2009年12月3日,它带来了一些新的,改善现有的Array数组操作的方法.然而,这些新奇的数组方法并没有真正流行起来的,因为当时市场上缺乏支持ES5的浏览器.     ...

  9. HBase Filter 过滤器之RowFilter详解

    前言:本文详细介绍了HBase RowFilter过滤器Java&Shell API的使用,并贴出了相关示例代码以供参考.RowFilter 基于行键进行过滤,在工作中涉及到需要通过HBase ...

随机推荐

  1. error.c

    #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> ...

  2. 可见参数和增强for以及自动拆装箱

    可变参数:定义方法的时候不知道该定义多少个参数格式: 修饰符 返回值类型 方法名(数据类型… 变量名){ } 注意: 这里的变量其实是一个数组如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定 ...

  3. linux 普通用户下使用 jdk 、Tomcat

    需求: 在已经跑的 1.7java环境中需要跑1.8java环境所需要的tomcat,那么因为java环境的不同,nginx代理会出现很多问题,tomcat根本跑不起来,所以提供了以下解决方案. 一, ...

  4. Spark Streaming连接Kafka的两种方式 direct 跟receiver 方式接收数据的区别

    Receiver是使用Kafka的高层次Consumer API来实现的. Receiver从Kafka中获取的数据都是存储在Spark Executor的内存中的,然后Spark Streaming ...

  5. 阿里云CentOS7.2卸载CDH5.12

    #####################删除前最好将之前的下载的rpm包和parcels包备份一下##################### 1.停止serviceservice cloudera- ...

  6. Centos7下安装小米SQL优化工具SOAR

    1 下载源码包 赋予权限 wget https://github.com/XiaoMi/soar/releases/download/0.9.0/soar.linux-amd64 -O soar ch ...

  7. git详细介绍

    Git管理我们的代码会经历三个不过程 1. 工作区:没有提交的代码就是存放的工作区 2. 暂存区:通过 git add 文件名 命令提交代码该文件就放在暂存区 3. 历史区:通过 git commit ...

  8. 分页插件通用处理,以asp.net mvc为例

    Model: public class PaggerModel { public PaggerModel() { BarSize = ; } public PaggerModel(int total, ...

  9. Spring基础2

    一.Spring属性注入 1)构造方法属性注入 2)set方法属性注入:通过在bean对象所属类中提供相应字段的set方法,并在配置文件中配置<property.....> <bea ...

  10. mysql 原理 ~ DDL之在线DDL

    一 简介:今天来DDL的变革二 DDL演化方式:  1 copy table : 1 创建临时表2 copy数据到临时表 3 rename进行交换 缺点 1 阻塞事务 2占用磁盘空间  2 inpla ...