直方图聚合是一个用于评估数值型或是数值范围型价值的文档的多桶(multi-bucket)聚合,它可以对参与聚合的值动态生成固定尺寸的桶。比如,如果一些文档具有数值型字段“price”,我们可以配置聚合间隔为5(在价钱中可能为5元)来动态生成直方图统计。当聚合执行的时候,每个文档的price字段会参与估算,并且为四舍五入到最近的桶中。比如,如果一个文档的price字段值为32,桶的尺寸为5,并且字段值四舍五入后的值为30,那么这个文档就会归入跟30这个关键字关联的桶内。下面的算式可以精确的确定每个文档的归属桶(根据桶的关键字确定)。
bucket_key = Math.floor((value - offset) / interval) * interval + offset
注意:interval必须是是一个十进制的正数,同时offset必须是[0,interval)(一个大于等于0、小于interval的十进制数)之间的一个十进制的数。
对字段类型是范围类型的文档可能归属于不同的桶,第一个桶bucket_key_min是由文档的值域的最小值根据上述的算法计算而得,最后一个桶bucket_key_max是根据值域的最大值根据上述的算法计算而得,那么这个文档归属的桶范围为[bucket_key_min,bucket_key_max]。
例子
POST /sales/_search?size=0
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50
}
}
}
}
上面的代码是对sales索引中的所有文档根据“price”字段,以50为间隔进行归类统计。返回的结果如下:
{
...
"aggregations": {
"prices" : {
"buckets": [
{
"key": 0.0,
"doc_count": 1
},
{
"key": 50.0,
"doc_count": 1
},
{
"key": 100.0,
"doc_count": 0
},
{
"key": 150.0,
"doc_count": 2
},
{
"key": 200.0,
"doc_count": 3
}
]
}
}
}
最少文档数目
上面的例子中,从返回结果我们可以了解到,没有文档的price字段值落在在[100,150)之间,所以返回值为0。默认情况下,Elasticsearch会返回文档数目为0的桶的信息。我们可以通过设置min_doc_count参数值来指定只有包含min_doc_count数目文档的桶才会返回信息。比如,我们设置min_doc_count值为1后,那么只有聚合后包含有1个文档以上的桶的信息才会被返回。
POST /sales/_search?size=0
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"min_doc_count" : 1
}
}
}
}
返回结果是:
{
...
"aggregations": {
"prices" : {
"buckets": [
{
"key": 0.0,
"doc_count": 1
},
{
"key": 50.0,
"doc_count": 1
},
{
"key": 150.0,
"doc_count": 2
},
{
"key": 200.0,
"doc_count": 3
}
]
}
}
}
默认情况下,histogram聚合会返回包含参与聚合的数据范围的所有的桶,也就是说,参与聚合的字段值最小的文档决定了最小的桶(具有最小关键字的桶),参与聚合的字段值最大的文档决定了最大的桶(具有最大关键字的桶)。在请求空的桶的时候,如果数据一起被过滤,会造成直方聚合一定的不确定性。
为了更好的理解,我们可以查看一下下面的例子:
POST /sales/_search?size=0
{
"query" : {
"constant_score" : { "filter": { "range" : { "price" : { "to" : "500" } } } }
},
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"extended_bounds" : {
"min" : 0,
"max" : 500
}
}
}
}
}
假如我们要在所有的文档中检索值在0-500,另外我们想使用histogram聚合将每个文档按照50的间隔进行直方图聚合。同时,为了获取包括空桶在内的所有桶的信息,我们设定min_doc_count值为0。上面说的不确定性,为在这种情况下发生。如果所有文档的参与聚合的字段值都大于100,那么第一个桶会以100作为桶的关键字,而我们是想同时获取0-100之间的桶的信息。
当参数“extended_bounds”设置后,即使在参与聚合的字段值不是落在间隔统计范围的情况下,elasticsearch也会迫使histogram聚合从一个特定的min(最小值)开始构建桶一直到最大值(max)为止。使用extended_bounds参数可以使min_doc_count值为0的情况下返回什么内容更加明确。
需要注意的是,extended_bounds不会过滤桶。这就意味着,如果extended_bounds.min的值比参与聚合文档的最小值来的大,这个文档就会决定最小桶的内容和关键字,同样的,如果extended_bounds.max的值比参与聚合文档的最大值来的小,这个文档就会决定最大桶的内容和关键字。如果要过滤桶的话,我们应该在filter聚合中嵌入直方图聚合,并且通过设置合适from、to参数值来限定范围。
当聚合一个范围值时,聚合操作都是基于返回的文档值进行的,这表示返回结果中可能包含查询范围外的桶的信息。比如,我们正在检索大于100的值,而设定聚合的范围为50-150、间隔为50,那么文档就可以落在50、100、150这3个桶(范围)内。我们可以把检索和聚合的步骤是独立的,查询操作选取了符合条件的文档集合,而聚合则不管这些文档是如何选中的,只是对这些查询到的文档进行桶筛选。
排序(Order)
默认情况下,histogram聚合返回结果以桶的key值进行升序排列,但是可以通过修改order参数值来更改排序方式,order的设定跟terms aggregation中的Order参数相同。
移位(Offset)
默认情况下,桶的关键字从0开始,并以interval为相同间隔递进。比如,interval的值为10,前3个桶(如果桶内有数据的话)会是[0,10),[10,20),[2,30)。桶的边界可以通过offset值的修改发生更改。
假如现在有10个文档,他们的范围在5-14之间,那么interval为10的话会构建2个桶(分别为[0,10),[10,20)),并且每个桶内都有5个文档。现在我们设置offset为5,那么就只会构建一个桶([5,15))就可以包括10个文档了。
返回格式(Response format)
默认情况下,histogram聚合返回的桶信息是以桶的关键字排序后的数组,我们可以通过设置“keyed”参数为“true”,限定桶以keyed为关键字进行哈希形式返回。
POST /sales/_search?size=0
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 50,
"keyed" : true
}
}
}
}
返回结果为:
{
...
"aggregations": {
"prices": {
"buckets": {
"0.0": {
"key": 0.0,
"doc_count": 1
},
"50.0": {
"key": 50.0,
"doc_count": 1
},
"100.0": {
"key": 100.0,
"doc_count": 0
},
"150.0": {
"key": 150.0,
"doc_count": 2
},
"200.0": {
"key": 200.0,
"doc_count": 3
}
}
}
}
}
缺省值
“missing”参数指定了文档缺失时的处理值。默认情况下,缺失的文档会被忽略,但是我们可以通过设置missing参数使其具有具体的值。
POST /sales/_search?size=0
{
"aggs" : {
"quantity" : {
"histogram" : {
"field" : "quantity",
"interval": 10,
"missing": 0
}
}
}
}
上面的例子中,如果文档的quantity字段没有值,那么这个文档跟quantity值为0的文档一样处理,归到文档值为0的桶内。