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

介绍

聚合(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();
        }
    }
}
相关推荐
岛雨QA23 分钟前
通俗易懂学习Java反射
java·后端
不吃元西1 小时前
反射机制的理解
java
汤姆yu1 小时前
基于springboot的美食分享推荐系统
java·spring boot·后端·美食
2301_804774491 小时前
算法题练习
java·开发语言·算法
失业写写八股文1 小时前
Java常量池的三驾马车:全面解析Class、运行期与字符串常量池
java·jvm
真的睡不醒ovo1 小时前
关于解决新版本spring项目请求测试接口返回406的问题
java·后端·spring
砸吧砸吧1 小时前
#mapreduce打包#maven:could not resolve dependencies for project
java·大数据·maven·mapreduce
chen2017sheng1 小时前
什么是J2EE应用服务器?
java·java-ee
@Arielle。2 小时前
【Excel】- 导入报错Can not find ‘Converter‘ support class LocalDateTime
java·后端·excel
天草二十六_简村人2 小时前
kong搭建一套微信小程序的公司研发环境
java·后端·微信小程序·小程序·kong