[微服务]分布式搜索--数据聚合

介绍

聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析、运算。

例如:

  • 什么品牌的手机最受欢迎?
  • 这些手机的平均价格、最高价格、最低价格?
  • 这些手机每月的销售情况如何?
  1. 实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。

  2. 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.12/search-aggregations.html

  3. 聚合常见的有三类:

  • 桶( Bucket**)**聚合:用来对文档做分组
    • TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照国家分组
    • Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
  • 度量( Metric**)**聚合:用以计算一些值,比如:最大值、最小值、平均值等
    • Avg:求平均值
    • Max:求最大值
    • Min:求最小值
    • Stats:同时求maxminavgsum
  • 管道( pipeline**)**聚合:其它聚合的结果为基础做进一步运算
  1. **注意:**参加聚合的字段必须是keyword、日期、数值、布尔类型的字段

DSL聚合

Bucket聚合

例如我们要统计所有商品中共有哪些商品分类,其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。

基本语法如下:

示例:

GET /items/_search
{
    "size": 0, 
    "aggs": {
        "category_agg": {
            "terms": {
                "field": "category",
                "size": 20
            }
        },
        # 如果需要多个字段, 继续添加
        "brand_agg": {
           "terms": {
              "field": "brand",
              "size": 10
             }
        }
    }
}

结果:

带条件聚合

默认情况下,Bucket聚合是对索引库的所有文档做聚合,可以限定要聚合的文档范围,添加query条件即可

例如,我想知道价格高于3000元的手机品牌有哪些:

  • 搜索查询条件:
    • 价格高于3000
    • 必须是手机
  • 聚合目标:统计的是品牌,肯定是对brand字段做term聚合

语法如下:

聚合结果:

{
  "took" : 2,
  "timed_out" : false,
  "hits" : {
    "total" : {
      "value" : 13,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "brand_agg" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "华为",
          "doc_count" : 7
        },
        {
          "key" : "Apple",
          "doc_count" : 5
        },
        {
          "key" : "小米",
          "doc_count" : 1
        }
      ]
    }
  }
}

可以看到,结果中只剩下3个品牌了。

Metric聚合

除了对数据分组(Bucket)以外,我们还可以对每个Bucket内的数据进一步做数据计算和统计

例如: 我想知道手机有哪些品牌,每个品牌的价格最小值、最大值、平均值。

这就要用到Metric聚合了,例如stat聚合,就可以同时获取minmaxavg等结果。

语法如下:

  1. query部分就不说了,我们重点解读聚合部分语法。
  2. 可以看到我们在brand_agg聚合的内部,我们新加了一个aggs参数。这个聚合就是brand_agg的子聚合,会对brand_agg形成的每个桶中的文档分别统计。
  • stats_meric:聚合名称
  • stats:聚合类型,stats是metric聚合的一种
  • field:聚合字段,这里选择price,统计价格
  1. 由于stats是对brand_agg形成的每个品牌桶内文档分别做统计,因此每个品牌都会统计出自己的价格最小、最大、平均值。
  2. 结果如下:
  1. 另外,我们还可以让聚合按照每个品牌的价格平均值排序:

总结

aggs代表聚合,与query同级,此时query的作用是?

  • 限定聚合的的文档范围

聚合必须的三要素:

  • 聚合名称
  • 聚合类型
  • 聚合字段

聚合可配置属性有:

  • size:指定聚合结果数量
  • order:指定聚合结果排序方式
  • field:指定聚合字段

RestClient聚合

可以看到在DSL中,aggs聚合条件与query条件是同一级别,都属于查询JSON参数。因此依然是利用request.source()方法来设置。

不过聚合条件的要利用AggregationBuilders这个工具类来构造。DSL与JavaAPI的语法对比如下:

我们以品牌聚合为例:

聚合结果与搜索文档同一级别,因此需要单独获取和解析。具体解析语法如下:

完整代码如下:

public class ElasticSearchTest {

    private RestHighLevelClient client;

    @Test
    void test() {
        System.out.println("client =" + client);
    }

    @Test
    void testAgg() throws IOException {
        //1.创建request对象
        SearchRequest request = new SearchRequest("items");
        //2.组织DSL条件
        //2.1分页参数, 设置0不返回文档数据
        request.source().size(0);
        //2.2.聚合条件
        request.source().aggregation(
            AggregationBuilders.terms("brandAgg").field("price").size(10)
        );
        //3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //4.解析结果
        System.out.println("response = " + response);
        Aggregations aggregations = response.getAggregations();
        // 4.1根据聚合名称获取对应的聚合
        Terms brandTerms = aggregations.get("brandAgg");
        // 4.2获取buckets
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        // 4.3遍历获取每一个bucket
        for (Terms.Bucket bucket : buckets) {
            System.out.println("price:" + bucket.getKeyAsString());
            System.out.println("count:" + bucket.getDocCount());
        }

    }


    @BeforeEach
    void setup() {
        client = new RestHighLevelClient(RestClient.builder(
            HttpHost.create("http://192.168.1.97:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        if (client != null) {
            client.close();
        }
    }
}
相关推荐
forestsea3 分钟前
【Elasticsearch】文档操作:添加、更新和删除
大数据·elasticsearch·搜索引擎
小大力7 分钟前
简单的jmeter数据请求学习
java·学习·jmeter
Anna_Tong12 分钟前
提升数据分析能力,解锁智能商业:阿里云检索分析服务 Elasticsearch 版
elasticsearch·阿里云·数据分析
孑么13 分钟前
力扣 二叉树的最大深度
java·算法·leetcode·职场和发展·深度优先·广度优先
mikey棒棒棒15 分钟前
SSM-Spring-IOC/DI注解开发
java·后端·spring·ssm·ioc·di
xweiran35 分钟前
Spring源码分析之事件机制——观察者模式(二)
java·开发语言·spring·观察者模式·底层源码
深鱼~38 分钟前
【多线程初阶篇¹】线程理解| 线程和进程的区别
java·开发语言·人工智能·深度学习·计算机视觉
Q_19284999061 小时前
基于Spring Boot的前后端分离的外卖点餐系统
java·spring boot·后端
xmh-sxh-13141 小时前
Redis中字符串和列表的区别
java
一个单纯的少年1 小时前
PYTHON与JAVA执行时间对比
java·开发语言·python