Elastic Search笔记

1.简介

Elastic Search是一个分布式的全文检索工具,可以用在商城中检索商品信息等。

接下来介绍本文需要用的三个工具,这三个工具版本号要相等,我选用的全部是6.8版本。

安装过程不再赘述,注意一点:ES的客户端端口号默认是9200,集群节点通信端口是9300,安装完后要打开linux防火墙上的对应端口。

2.概念和工具使用

2.1 基本概念

阮一峰的博客里面讲的比较好:http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html

我自己的理解

ES的存储结构:索引下面包含许多文档,每个文档就是一条数据,类型把文档逻辑分组。

搜索就是根据过滤条件查询文档的过程。

我对类型的概念不是特别理解,接下来实践后再说,另外这个概念在ES7以后要被废除掉。

除了Node、Cluster、Index、Document、Type这几个概念,还有分片(shard)和副本(replica)两个概念。

分片就是把整体的数据切割成几片,目的是为了在数据量大的时候分流;

副本是每个分片的备份,如果出现意外导致数据丢失,还能指望一下备份。

下图表示:3个分片,每个分片有1个副本。

2.2 使用kibana

ES的API都是Rest风格的,请求和响应都是json格式。

kibana中提供了开发工具,可以很方便地发送请求,接收数据,还有语法提示:

接下来的演示都是在kibana中操作的。

3.操作索引和数据

2.3 索引

2.3.1 创建索引

使用PUT请求创建一个名叫test_index的索引,有3个分片,2个副本。

PUT test_index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
  }
}

图示:

2.3.2 查看索引设置

使用 GET 索引名 即可查询

GET test_index

结果图示,可以看到创建时间,分片和副本信息等。

2.3.3 删除索引

使用 DELETE 索引名 即可删除索引

DELETE test_index

2.4 索引映射到文档

2.4.1 创建映射

语法

PUT /索引库名/_mapping/类型名称
{
  "properties": {
    "字段名": {
      "type": "类型",
      "index": true,
      "store": true,
      "analyzer": "分词器"
    }
  }
}
  • 类型名称:相当于把文档逻辑分组。
  • 字段名:文档中的字段名,比如title、price等等。
  • type:字段类型,比如text、long、integer、object等等。
  • index:是否索引,默认为true
  • store:是否存储,默认为false
  • analyzer:分词器类型。

示例:

PUT test_index/_mapping/goods
{
  "properties": {
    "title": {
      "type": "text",
      "analyzer": "ik_max_word"
    },
    "images": {
      "type": "keyword",
      "index": "false"
    },
    "price": {
      "type": "float"
    }
  }
}

2.4.2 查看映射

发送请求

GET /test_index/_mapping

得到结果

{
  "test_index" : {
    "mappings" : {
      "goods" : {
        "properties" : {
          "images" : {
            "type" : "keyword",
            "index" : false
          },
          "price" : {
            "type" : "float"
          },
          "title" : {
            "type" : "text",
            "analyzer" : "ik_max_word"
          }
        }
      }
    }
  }
}

2.4.3 字段属性详解

2.4.3.1 type
  • String类型,又分两种:

    • text:可分词,不可参与聚合
    • keyword:不可分词,数据会作为完整字段进行匹配,可以参与聚合
  • Numerical:数值类型,分两类

    • 基本数据类型:long、integer、short、byte、double、float、half_float
    • 浮点数的高精度类型:scaled_float
      • 需要指定一个精度因子,比如10或100。ES会把真实值乘以这个因子后存储,取出时再还原。
  • Date:日期类型

    ES可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。

  • 如果是对象

    比如{girl:{name:"rose", age:21}},会处理成两个字段girl.name,girl.age

2.4.3.2 index

index影响字段的索引情况。

  • true:字段会被索引,则可以用来进行搜索。默认值就是true
  • false:字段不会被索引,不能用来搜索

index的默认值就是true,也就是说你不进行任何配置,所有字段都会被索引。

但是有些字段是我们不希望被索引的,比如商品的图片信息,就需要手动设置index为false。

值得注意的一个问题,不能用来搜索的字段,存在ES中用来干嘛呢?

是不是就把ES当成数据库了,查出来的数据要直接能用。

2.4.3.3 store

Elasticsearch在创建文档索引时,会将文档中的原始数据备份,保存到一个叫做_source的属性中。而且我们可以通过过滤_source来选择哪些要显示,哪些不显示。

而如果设置store为true,就会在_source以外额外存储一份数据,多余,因此一般我们都会将store设置为false,事实上,store的默认值就是false。

2.5 新增数据

格式如下,如果不定义ID,则会创建一个随机ID

POST /索引/类型/ID
{
    文档内容
}

示例

POST /test_index/goods/1
{
    "title":"小米手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2699.00
}

2.5.1 智能添加字段

如果添加的数据中有未定义的字段,ES会自动添加

POST /test_index/goods/100
{
    "title":"超米手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2899.00,
    "stock": 200,
    "saleable":true
}

添加后查询结果如图所示:

添加的字段不会影响到其他已经存在的数据的_source

但是索引的映射结构会变化

存入的数据包含什么字段,_source就会包含什么字段。

2.6 修改数据

发送方式改为PUT,指定Id即可修改数据

  • id对应文档存在,则修改
  • id对应文档不存在,则新增

示例

PUT /test_index/goods/100
{
    "title":"超级大米手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2899.00,
    "stock": 200
}

2.7 删除数据

语法

DELETE /索引库名/类型名/id值

例子

DELETE /test_index/goods/3

4. 搜索

先存点数据:

POST /test_index/goods/1
{
    "title":"小米手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2699.00
}

POST /test_index/goods/2
{
    "title":"大米手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2899.00
}

PUT /test_index/goods/3
{
    "title":"小米电视4A",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":3899.00
}

接下来开始重头戏:花式查询

4.1 查询

4.1.1 match_all(查询所有)

GET /test_index/_search
{
    "query":{
        "match_all": {}
    }
}

结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "title" : "大米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2899.0
        }
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "title" : "小米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2699.0
        }
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "title" : "小米电视4A",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 3899.0
        }
      }
    ]
  }
}

注意结果里面的_score,这是文档相关性得分,得分越高说明越符合搜索条件。

4.1.2 match(匹配查询)

单字段查询:OR关系

把小米电视分成小米和电视两个词分别查询,多个词语的查询条件是or的关系

相当于title like '%小米%' or title like '%电视%'

关键词命中越多,搜索得分越高,结果越靠前

GET /test_index/_search
{
    "query":{
        "match":{
            "title":"小米电视"
        }
    }
}

结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.77041245,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "3",
        "_score" : 0.77041245,
        "_source" : {
          "title" : "小米电视4A",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 3899.0
        }
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "1",
        "_score" : 0.21110918,
        "_source" : {
          "title" : "小米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2699.0
        }
      }
    ]
  }
}

单字段查询:AND关系

小米和电视两个词,查询条件用and组合起来,相当于title like '%小米%' and title like '%电视%'

GET /test_index/_search
{
    "query":{
        "match": {
          "title": {
            "query": "小米电视",
            "operator": "and"
          }
        }
    }
}

结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.77041245,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "3",
        "_score" : 0.77041245,
        "_source" : {
          "title" : "小米电视4A",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 3899.0
        }
      }
    ]
  }
}

单字段查询:匹配度

“小米曲面电视” 在ik_max_word的设置下,会被分为小米、曲面、电视三个词。

如果需要查到能够匹配其中两个词语的结果,设置匹配度>=(2/3)即可。

实验表明,设置67%的查询结果为“小米电视4A”;66%的查询结果为“小米电视4A”和“小米手机”

GET /test_index/_search
{
    "query":{
        "match":{
            "title":{
            	"query":"小米曲面电视",
            	"minimum_should_match": "67%"
            }
        }
    }
}

结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.77041245,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "3",
        "_score" : 0.77041245,
        "_source" : {
          "title" : "小米电视4A",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 3899.0
        }
      }
    ]
  }
}

4.1.3 multi_match(多字段查询)

在title和subTitle两个字段都匹配

GET /test_index/_search
{
    "query":{
        "multi_match": {
            "query":    "小米",
            "fields":   [ "title", "subTitle" ]
        }
	}
}

4.1.4 term(精确匹配)

查询price=2699.00的数据

GET /test_index/_search
{
    "query":{
        "term":{
            "price":2699.00
        }
    }
}

4.1.5 terms(多词条精确匹配)

查询price=数组中的任何一个数字的结果

GET /test_index/_search
{
    "query":{
        "terms":{
            "price":[2699.00,2899.00,3899.00]
        }
    }
}

4.1.6 bool(布尔查询)

  • must 与
  • must_not 非
  • should 或

下面的查询是要找:title字段中必须包含“大米”,必须不包含“电视”,可以包含“手机”的结果。

GET /test_index/_search
{
    "query":{
        "bool":{
        	"must":     { "match": { "title": "大米" }},
        	"must_not": { "match": { "title":  "电视" }},
        	"should":   { "match": { "title": "手机" }}
        }
    }
}

结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "2",
        "_score" : 0.5753642,
        "_source" : {
          "title" : "大米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2899.0
        }
      }
    ]
  }
}

4.1.7 range(范围查询)

一般是数值和时间范围的查询

操作符 说明
gt 大于
gte 大于等于
lt 小于
lte 小于等于

例子:查询price>=1000 and price < 2800的结果

GET /test_index/_search
{
    "query":{
        "range": {
            "price": {
                "gte":  1000.0,
                "lt":   2800.00
            }
    	}
    }
}

4.1.8 fuzzy(模糊查询)

允许输入内容有些偏差,但还能返回正确的结果:比如输入了appla却能够查到apple

例子:

新增商品“apple手机”

POST /test_index/goods/4
{
    "title":"apple手机",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":6899.00
}

模糊查询,设置偏移量为2,即偏差<=2

GET /test_index/_search
{
  "query": {
    "fuzzy": {
        "title": {
            "value":"appla",
            "fuzziness":2
        }
    }
  }
}

结果是能找到apple手机。

4.1.9 结果字段的显示

  • 指定返回结果的字段为title和price
GET /test_index/_search
{
  "_source": ["title","price"],
  "query": {
    "term": {
      "price": 2699
    }
  }
}

返回结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "price" : 2699.0,
          "title" : "小米手机"
        }
      }
    ]
  }
}
  • 指定includes和excludes

includes指定包含的字段,excludes指定要排除的字段。

例如

GET /test_index/_search
{
  "_source": {
    "includes":["title","price"]
  },
  "query": {
    "term": {
      "price": 2699
    }
  }
}

GET /test_index/_search
{
  "_source": {
    "excludes": ["images"]
  },
  "query": {
    "term": {
      "price": 2699
    }
  }
}

4.2 过滤

查询和过滤有何区别?

参考这篇博客:[https://blog.csdn.net/en_joker/article/details/78017306

所有的查询都会影响到文档的评分及排名。如果我们需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用。而是使用filter方式:

GET /test_index/_search
{
    "query":{
        "bool":{
        	"must":{ "match": { "title": "小米手机" }},
        	"filter":{
                "range":{"price":{"gt":2000.00,"lt":3800.00}}
        	}
        }
    }
}

注意:filter中还可以再次进行bool组合条件过滤。

如果一次查询只有过滤,没有查询条件,不希望进行评分,我们可以使用constant_score取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。

GET /test_index/_search
{
    "query":{
        "constant_score":   {
            "filter": {
            	 "range":{"price":{"gt":2000.00,"lt":3000.00}}
            }
        }
}

查询相比于过滤,最重要的特点是:关注相关性

4.3 排序

4.3.1 单字段排序

GET /test_index/_search
{
  "query": {
    "match": {
      "title": "小米手机"
    }
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

结果

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 4,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "4",
        "_score" : null,
        "_source" : {
          "title" : "apple手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 6899.0
        },
        "sort" : [
          6899.0
        ]
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "3",
        "_score" : null,
        "_source" : {
          "title" : "小米电视4A",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 3899.0
        },
        "sort" : [
          3899.0
        ]
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "title" : "大米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2899.0
        },
        "sort" : [
          2899.0
        ]
      },
      {
        "_index" : "test_index",
        "_type" : "goods",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "title" : "小米手机",
          "images" : "http://image.leyou.com/12479122.jpg",
          "price" : 2699.0
        },
        "sort" : [
          2699.0
        ]
      }
    ]
  }
}

4.3.2 多字段排序

查询结果先按照价格排序,再按照相关性得分排序

GET /test_index/_search
{
    "query":{
        "bool":{
        	"must":{ "match": { "title": "小米手机" }},
        	"filter":{
                "range":{"price":{"gt":2,"lt":300000}}
        	}
        }
    },
    "sort": [
      { "price": { "order": "desc" }},
      { "_score": { "order": "desc" }}
    ]
}

结果不再展示

5. 聚合

聚合可以让我们极其方便的实现对数据的统计、分析。例如:

  • 什么品牌的手机最受欢迎?
  • 这些手机的平均价格、最高价格、最低价格?
  • 这些手机每月的销售情况如何?

实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现实时搜索效果。

5.1 基本概念

桶(bucket)

桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个,例如我们根据国籍对人划分,可以得到中国桶英国桶日本桶……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。

Elasticsearch中提供的划分桶的方式有很多:

  • Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
  • Histogram Aggregation:根据数值阶梯分组,与日期类似
  • Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
  • Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
  • ……

度量(metrics)

分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量

比较常用的一些度量聚合方式:

  • Avg Aggregation:求平均值
  • Max Aggregation:求最大值
  • Min Aggregation:求最小值
  • Percentiles Aggregation:求百分比
  • Stats Aggregation:同时返回avg、max、min、sum、count等
  • Sum Aggregation:求和
  • Top hits Aggregation:求前几
  • Value Count Aggregation:求总数
  • ……

注意:在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,不能被分词。比如字符串的类型必须为keyword,而不是text,因为text能被分词。

5.2 导入数据

导入汽车销售统计数据

先创建索引

PUT /cars
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "transactions": {
      "properties": {
        "color": {
          "type": "keyword"
        },
        "make": {
          "type": "keyword"
        }
      }
    }
  }
}

批量导入数据

POST /cars/transactions/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }

5.3 聚合为桶

下面的例子演示:统计每种颜色的汽车销量

GET /cars/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color"
            }
        }
    }
}
  • size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
  • aggs:声明这是一个聚合查询,是aggregations的缩写
    • popular_colors:给这次聚合起一个名字,任意。
      • terms:划分桶的方式,这里是根据词条划分
        • field:划分桶的字段

结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_colors" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "red",
          "doc_count" : 4
        },
        {
          "key" : "blue",
          "doc_count" : 2
        },
        {
          "key" : "green",
          "doc_count" : 2
        }
      ]
    }
  }
}

  • hits:查询结果为空,因为我们设置了size为0
  • aggregations:聚合的结果
  • popular_colors:我们定义的聚合名称
  • buckets:查找到的桶,每个不同的color字段值都会形成一个桶
    • key:这个桶对应的color字段的值
    • doc_count:这个桶中的文档数量

观察结果可以发现红色小车最畅销。

5.4 桶内度量

5.3中只是对数据进行了聚合操作,但通常在聚合之后还要进行度量,比如查询每种颜色的车的价格平均值

发送请求

GET /cars/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color"
            },
            "aggs":{
                "avg_price": { 
                   "avg": {
                      "field": "price" 
                   }
                }
            }
        }
    }
}

得到结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_colors" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "red",
          "doc_count" : 4,
          "avg_price" : {
            "value" : 32500.0
          }
        },
        {
          "key" : "blue",
          "doc_count" : 2,
          "avg_price" : {
            "value" : 20000.0
          }
        },
        {
          "key" : "green",
          "doc_count" : 2,
          "avg_price" : {
            "value" : 21000.0
          }
        }
      ]
    }
  }
}

5.5 桶嵌套桶

在5.4统计条件的基础上,增加聚合条件:查询每种颜色的汽车分别都是哪几个品牌

GET /cars/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color"
            },
            "aggs":{
                "avg_price": { 
                   "avg": {
                      "field": "price" 
                   }
                },
                "maker":{
                    "terms":{
                        "field":"make"
                    }
                }
            }
        }
    }
}

结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_colors" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "red",
          "doc_count" : 4,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "honda",
                "doc_count" : 3
              },
              {
                "key" : "bmw",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 32500.0
          }
        },
        {
          "key" : "blue",
          "doc_count" : 2,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ford",
                "doc_count" : 1
              },
              {
                "key" : "toyota",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 20000.0
          }
        },
        {
          "key" : "green",
          "doc_count" : 2,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ford",
                "doc_count" : 1
              },
              {
                "key" : "toyota",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 21000.0
          }
        }
      ]
    }
  }
}

可以看出来,红色车里面本田车最多。

5.6 其他划分桶的方式

前面是根据词条内容划分桶,还有很多其他的分桶方式,比如

Histogram(柱状图)分桶

直方图的X轴是按照固定间隔分开的,因此我们需要一个阶梯值(interval)来指定这个固定间隔。

示例

对汽车价格进行分组,指定间隔为5000,并且不显示统计数量为0的桶。

GET /cars/_search
{
  "size":0,
  "aggs":{
    "price":{
      "histogram": {
        "field": "price",
        "interval": 5000,
        "min_doc_count": 1
      }
    }
  }
}

结果

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "price" : {
      "buckets" : [
        {
          "key" : 10000.0,
          "doc_count" : 2
        },
        {
          "key" : 15000.0,
          "doc_count" : 1
        },
        {
          "key" : 20000.0,
          "doc_count" : 2
        },
        {
          "key" : 25000.0,
          "doc_count" : 1
        },
        {
          "key" : 30000.0,
          "doc_count" : 1
        },
        {
          "key" : 80000.0,
          "doc_count" : 1
        }
      ]
    }
  }
}

价格统计为:

  • [10000, 15000) : 2个
  • [15000, 20000) : 1个
  • [20000, 25000) : 2个
  • [25000, 30000) : 1个
  • [30000, 80000) : 1个
  • [80000, ...) : 1个
原文地址:https://www.cnblogs.com/qianbixin/p/11227649.html