ElasticSearch 聚合

1.聚合概念 aggs

  • ElasticSearch除了致力于搜索之外,也提供了聚合实时分析数据的功能
    • 如果把搜索比喻为大海捞针(从海量的文档中找出符合条件的那一个),那么聚合就是去分析大海中的针们的特性,像是
      • 在大海里有多少针?
      • 针的平均长度是多少?
      • 按照针的制造商来划分,针的长度中位值是多少?
      • 每月加入到海中的针有多少?
      • 这里面有异常的针么?
    • 因此透过聚合,我们可以得到一个数据的概览,聚合能做的是分析和总结全套的数据,而不是查找单个文档(这是搜索做的事)
      • 聚合允许我们向数据提出一些复杂的问题,虽然他的功能完全不同于搜索,但他们其实使用了相同的数据结构,这表示聚合的执行速度很快,并且就像搜索一样几乎是实时的
    • 并且由于聚合和搜索是使用同样的数据结构,因此聚合和搜索可以是一起执行的
    • 这表示我们可以在一次json请求里,同时对相同的数据进行搜索/过滤 + 分析,两个愿望一次满足
  • 聚合的两个主要的概念,分别是指标
    • 桶(Buckets): 满足特定条件的文档的集合
      • 当聚合开始被执行,每个文档会决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着进行聚合操作
        • 像是一个员工属于男性桶或者女性桶,日期2014-10-28属于十月桶,也属于2014年桶
      • 桶可以被嵌套在其他桶里面
        • 像是北京能放在中国桶里,而中国桶能放在亚洲桶里
      • Elasticsearch提供了很多种类型的桶,像是时间、最受欢迎的词、年龄区间、地理位置桶等等,不过他们在根本上都是通过同样的原理进行操作,也就是基于条件来划分文档,一个文档只要符合条件,就可以加入那个桶,因此一个文档可以同时加入很多桶
    • 指标(Metrics): 对桶内的文档进行统计计算
      • 桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算
      • 指标通常是简单的数学运算(像是min、max、avg、sum),而这些是通过当前桶中的文档的值来计算的,利用指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据
        • aggs 聚合的模板
    • 当query和aggs一起存在时,会先执行query的主查询,主查询query执行完后会搜出一批结果,而这些结果才会被拿去aggs拿去做聚合
      • 另外要注意aggs后面会先接一层自定义的这个聚合的名字,然后才是接上要使用的聚合桶
      • 如果有些情况不在意查询结果是什么,而只在意aggs的结果,可以把size设为0,如此可以让返回的hits结果集是0,加快返回的速度
    • 一个aggs里可以有很多个聚合,每个聚合彼此间都是独立的,因此可以一个聚合拿来统计数量、一个聚合拿来分析数据、一个聚合拿来计算标准差...,让一次搜索就可以把想要做的事情一次做完
      • 像是此例就定义了3个聚合,分别是custom_name1、custom_name2、custom_name3
    • aggs可以嵌套在其他的aggs里面,而嵌套的桶能作用的文档集范围,是外层的桶所输出的结果集
        GET 127.0.0.1/mytest/doc/_search
        {
            "query": { ... },
            "size": 0,
            "aggs": {
                "custom_name1": { // aggs后面接着的是一个自定义的name
                    "桶": { ... } // 再来才是接桶
                },
                "custom_name2": { // 一个aggs里可以有很多聚合
                    "桶": { ... }
                },
                "custom_name3": {
                    "桶": { ... },
                    "aggs": { // aggs可以嵌套在别的aggs里面
                        "in_name": { // 记得使用aggs需要先自定义一个name
                            "桶": { ... } // in_name的桶作用的文档是custom_name3的桶的结果
                        }
                    }
                }
            }
        }
      
    • 结果
        {
            "hits": {
                "total": 8,
                "max_score": 0,
                "hits": [] //因为size设为0,所以没有查询结果返回
            },
            "aggregations": {
                "custom_name1": {
                    ...
                },
                "custom_name2": {
                    ...
                },
                "custom_name3": {
                    ...,
                    "in_name": {
                        ....
                    }
                }
            }
        }
      

2.聚合中常用的桶 terms、filter、top_hits

2.1 介绍

2.1.1 terms桶

针对某个field的值进行分组,field有几种值就分成几组

  • terms桶在进行分组时,会为此field中的每种值创建一个新的桶
  • 要注意此"terms桶"和平常用在主查询query中的"查找terms"是不同的东西

2.1.2 filter桶

一个用来过滤的桶

  • 要注意此处的 "filter桶" 和用在主查询query的 "过滤filter" 的用法是一模一样的,都是过滤
  • 不过差别是 "filter桶" 会自己给创建一个新的桶,而不会像 "过滤filter" 一样依附在query下
    • 因为filter桶毕竟还是一个聚合桶,因此他可以和别的桶进行嵌套,但他不是依附在别的桶上

2.1.3 top_hits桶

在某个桶底下找出这个桶的前几笔hits,返回的hits格式和主查询query返回的hits格式一模一样

  • top_hits桶支持的参数
    • fromsize: 设置返回的文档数,和主查询query的from、size用法一样
    • sort: 设置返回的hits的排序
      • 要注意,假设在主查询query里已经对数据设置了排序sort,此sort并不会对aggs里面的数据造成影响,也就是说主查询query查找出来的数据会先丢进aggs而非先经过sort,因此就算主查询设置了sort,也不会影响aggs数据里的排序
      • 因此如果在top_hits桶里的返回的hits数据想要排序,需要自己在top_hits桶里设置sort
      • 如果没有设置sort,默认使用主查询query所查出来的_score排序
    • _source: 设置返回的字段

2.2 具体实例

2.2.1 准备数据

索引

{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 1
    },
    "mappings": {
        "goods": {
            "properties": {
                "color": {
                    "type": "string"
                },
                "price": {
                    "type": "integer"
                }
            }
        }
    }
}

数据查询

{
    "took": 70,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1,
        "hits": [
            {
                "_index": "shop",
                "_type": "goods",
                "_id": "AWYUBBbMnhQpAXBLM6DR",
                "_score": 1,
                "_source": {
                    "color": "red",
                    "price": 100
                }
            },
            {
                "_index": "shop",
                "_type": "goods",
                "_id": "AWYUBD5PnhQpAXBLM6DS",
                "_score": 1,
                "_source": {
                    "color": "green",
                    "price": 500
                }
            },
            {
                "_index": "shop",
                "_type": "goods",
                "_id": "AWYUBJCnnhQpAXBLM6DT",
                "_score": 1,
                "_source": {
                    "color": [
                        "red",
                        "blue"
                    ],
                    "price": 1000
                }
            }
        ]
    }
}

2.2.2 term聚合

{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_name": {
            "terms": {
                "field": "color",   // 使用color来进行分组
                "size": 100 //可以设置size,指定返回的桶数量,预设是10,如果总共桶数不超过100,那就会全部返回
            }
        },
        "my_price":{
            "terms": {
                "field": "color"
            },
            "aggs":{ // 嵌套两个指标avg、min在terms桶中
                "my_avg_price":{ // my_avg_price计算每个bucket的平均price
                    "avg":{
                        "field":"price"
                    }
                },
                "my_min_price":{ // my_min_price计算每个bucket中的最小price
                    "min":{
                        "field":"price"
                    }
                }
            }
        }
    }
}

2.2.3 filter桶

{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_color": { 
            "filter": { // 因为他用法跟一般的过滤filter一样,所以也能使用bool嵌套
                "bool":{
                    "must":{
                        "terms":{ // 注意此terms是查找terms,不是terms桶
                            "color": ["red", "blue"]
                        }
                    }
                }
            }
        },
        "my_color_aggs":{
            "filter": {  // filter桶
                "bool":{
                    "must":{
                        "terms":{
                            "color": ["red", "blue"]
                        }
                    }
                }
            },
            "aggs": {
                "my_name2": { 
                    "terms": {  // terms桶
                        "field": "color"                 
                    }                
                } 
            }
        }
    }
}

2.2.4 top_hits桶

{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_color": {
            "terms": {
                "field":"color"
            },
            "aggs": {
                "my_top_hits": {
                    "top_hits": {
                        "size": 5,
                        "sort": {
                            "price": "asc"
                        }
                    }
                }
            }
        }
    }
}

3.多桶排序

terms桶、histogram桶、data_histogram桶这些桶属于多值桶,也就是说他们会动态生成很多桶,对于这些生成出来的桶们,Elasticsearch默认会使用doc_value进行降序排序,也就是说哪个生成桶的doc_value文档数较多,哪个生成桶就排在前面

如果想要改变这个生成桶与生成桶之间的排序,可以在使用terms桶、histogram桶、data_histogram桶时,使用order进行排序

  • order支持的参数
    • _count: 按照文档数排序
    • _key: 按照每个桶的字符串值的字母顺序排序. Deprecated in 6.0.0. Use _key instead of _term to order buckets by their _term
{
    "query": {
        "match_all": {}
    },
    "size": 0,
    "aggs": {
        "my_sort": {
            "terms": {
                "field":"color",
                "order":{
                    "_term":"asc"
                }
            }
        }
    }
}

results matching ""

    No results matching ""