本文主要为您介绍多元索引的聚合接口。

类型

  • 字段值分组
    • 定义:在查询结果上根据一个字段的值进行分组,相同的值会放到一个分组内,最终返回每一个组的值和该值对应的个数。字段值分组在分组较大的情况下会存在极少量的误差。
    • 参数说明
      参数 说明
      groupByName 用户给本次统计聚合功能自定义的名字,用于区分不同的统计聚合操作。在使用结果时,需要根据该名字找到本次统计聚合的结果。
      fieldName 聚合作用的字段,仅支持keyword、long、double、bool类型。size:返回的分组数量。
      groupBySorter 添加分组中的item排序规则。默认按照分组中item的数量降序排序,多个排序则按照添加的顺序进行排列。支持的参数包括:
      • 按照key的字典序升序排列
      • 按照key的字典序降序排列
      • 按照行数升序排列
      • 按照行数降序排列
      • 按照子统计的值升序排列
      • 按照子统计的值降序排列
      size 返回的分组数量。
      subAggregation和subGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。举例:
      • 场景

        所有商品中每一个类别各有多少个?且统计每一个类别中再统计价格最大值和最小值。

      • 方法

        最外层的聚合是根据类别进行分组,然后添加两个子Aggregation求价格的最大值和最小值。

      • 结果示例
        • 水果:5个(其中价格最高15元,最低3元)
        • 洗漱用品:10个(其中价格最高98元,最低1元)
        • 电子设备:3个(其中价格最高8699元,最低2300元)
        • 其它:15个(其中价格最高1000元,最低80元)
    • Java示例
      /**
       * 所有商品中每一个类别各有多少个?且统计每一个类别的价格最大值和最小值。
       * 返回结果举例:"水果:5个(其中价格最贵15元,最便宜3元),洗漱用品:10个(其中价格最贵98元,最便宜1元),电子设备:3个(其中价格最贵8699元,最便宜2300元),其它:15个(其中价格最贵1000
       * 元,最便宜80元)"
       */
      public void groupByField(SyncClient client) {
          // 构建查询语句
          SearchRequest searchRequest = SearchRequest.newBuilder()
              .tableName("tableName")
              .indexName("indexName")
              .searchQuery(
                  SearchQuery.newBuilder()
                      .query(QueryBuilders.matchAll())
                      .limit(0)
                      .addGroupBy(GroupByBuilders
                          .groupByField("name1", "column_type")
                          .addSubAggregation(AggregationBuilders.min("subName1", "column_price"))
                          .addSubAggregation(AggregationBuilders.max("subName2", "column_price"))
                      )
                      .build())
              .build();
          //执行查询
          SearchResponse resp = client.search(searchRequest);
          //获取统计聚合的结果
          for (GroupByFieldResultItem item : resp.getGroupByResults().getAsGroupByFieldResult("name1").getGroupByFieldResultItems()) {
              // 值
              System.out.println(item.getKey());
              // 个数
              System.out.println(item.getRowCount());
              // 最低价格
              System.out.println(item.getSubAggregationResults().getAsMinAggregationResult("subName1").getValue());
              // 最高价格
              System.out.println(item.getSubAggregationResults().getAsMaxAggregationResult("subName2").getValue());
          }
      }
  • 数值范围分组
    • 定义:在查询结果上根据一个字段的范围进行分组,字段值在某范围内则会放到一个分组内,最终返回每一个范围中相应的item个数。例如求商品销量时候按[0,1k)、[1k,10k)、[0k,正无穷)进行分组计算每个范围的销量。
    • 参数说明
      参数 说明
      groupByName 用户给本次统计聚合功能自定义的名字,用于区分不同的统计聚合操作。在使用结果时,需要根据该名字找到本次统计聚合的结果。
      fieldName 聚合作用的字段,仅支持long、double类型。
      range[double_from, double_to) 添加分组的范围。起始值 from 可以使用最小值 Double.MIN_VALUE,结束值 to 可以使用最大值 Double.MAX_VALUE。
      subAggregation和subGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。例如:按销量分组后再按省份分组,即可获得某个销量段内哪个省比重比较大,其实现是 GroupByRange 内添加一个GroupByField。
    • Java示例
      /**
       * 求商品销量时候按[0,1k)、[1k,5k)、[5k,正无穷)进行分组计算每个范围的销量。
       */
      public void groupByRange(SyncClient client) {
          // 构建查询语句
          SearchRequest searchRequest = SearchRequest.newBuilder()
              .tableName("tableName")
              .indexName("indexName")
              .searchQuery(
                  SearchQuery.newBuilder()
                      .query(QueryBuilders.matchAll())
                      .limit(0)
                      .addGroupBy(GroupByBuilders
                          .groupByRange("name1", "column_number")
                          .addRange(0, 1000)
                          .addRange(1000, 5000)
                          .addRange(5000, Double.MAX_VALUE)
                      )
                      .build())
              .build();
          //执行查询
          SearchResponse resp = client.search(searchRequest);
          //获取统计聚合的结果
          for (GroupByRangeResultItem item : resp.getGroupByResults().getAsGroupByRangeResult("name1").getGroupByRangeResultItems()) {
              // 范围
              System.out.println(item.getKey());
              // 个数
              System.out.println(item.getRowCount());
          }
      }
  • 地理距离分组
    • 定义:在查询结果上根据距离某一个中心点的范围进行分组,距离差值在某范围内则会放到一个分组内,最终返回每一个范围中相应的item个数。例如求距离万达广场[0,1000米)、[1000米,5000米)、[5000米,正无穷)每个范围的人数。
    • 参数说明
      参数 说明
      groupByName 用户给本次统计聚合功能自定义的名字,用于区分不同的统计聚合操作。在使用结果时,需要根据该名字找到本次统计聚合的结果。
      fieldName 聚合作用的字段,仅支持geo_point类型。
      origin(double lat, double lon) lat 是起始中心点坐标纬度,lon 是起始中心点坐标经度。
      range[double_from, double_to) 添加分组的范围,单位为米。起始值 from 可以使用最小值 Double.MIN_VALUE,结束值 to 可以使用最大值 Double.MAX_VALUE。
      subAggregation和subGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。
    • Java示例
      /**
       * 求距离万达广场[0,1000米)、[1000米,5000米)、[5000米,正无穷)每个范围的人数。
       */
      public void groupByGeoDistance(SyncClient client) {
          // 构建查询语句
          SearchRequest searchRequest = SearchRequest.newBuilder()
              .tableName("tableName")
              .indexName("indexName")
              .searchQuery(
                  SearchQuery.newBuilder()
                      .query(QueryBuilders.matchAll())
                      .limit(0)
                      .addGroupBy(GroupByBuilders
                          .groupByGeoDistance("name1", "column_geo_point")
                          .origin(3.1, 6.5)
                          .addRange(0, 1000)
                          .addRange(1000, 5000)
                          .addRange(5000, Double.MAX_VALUE)
                      )
                      .build())
              .build();
          //执行查询
          SearchResponse resp = client.search(searchRequest);
          //获取统计聚合的结果
          for (GroupByGeoDistanceResultItem item : resp.getGroupByResults().getAsGroupByGeoDistanceResult("name1").getGroupByGeoDistanceResultItems()) {
              // 范围
              System.out.println(item.getKey());
              // 个数
              System.out.println(item.getRowCount());
          }
      }
  • Filter分组
    • 定义:在查询结果上,按照Filter(即Query)进行分组,然后获得每个Filter匹配到的数量。返回的结果顺序和添加的Filter顺序一致。例如添加三个Filter(销量大于100、产地是浙江、描述中包含浙江关键词),然后获得这三个Filter匹配到的数量。
    • 参数说明
      参数 说明
      groupByName 用户给本次统计聚合功能命名的名字,用于区分不同的统计聚合操作。在使用结果时候,需要根据该名字找到本次统计聚合的结果。
      filter 添加的Filter(Query),使用QueryBuilders进行构建。
      subAggregation和subGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。
    • Java示例
      /**
       * 按照Filter(即Query)进行分组,例如添加三个Filter(销量大于100、产地是浙江、描述中包含浙江关键词),然后获得每个Filter匹配到的数量。
       */
      public void groupByFilter(SyncClient client) {
          // 构建查询语句
          SearchRequest searchRequest = SearchRequest.newBuilder()
              .tableName("tableName")
              .indexName("indexName")
              .searchQuery(
                  SearchQuery.newBuilder()
                      .query(QueryBuilders.matchAll())
                      .limit(0)
                      .addGroupBy(GroupByBuilders
                          .groupByFilter("name1")
                          .addFilter(QueryBuilders.range("number").greaterThanOrEqual(100))
                          .addFilter(QueryBuilders.term("place","浙江省"))
                          .addFilter(QueryBuilders.match("text","杭州"))
                      )
                      .build())
              .build();
          //执行查询
          SearchResponse resp = client.search(searchRequest);
          //获取统计聚合的结果, 按照addFilter的顺序。
          for (GroupByFilterResultItem item : resp.getGroupByResults().getAsGroupByFilterResult("name1").getGroupByFilterResultItems()) {
              // 个数
              System.out.println(item.getRowCount());
          }
      }

多个统计聚合

每次请求支持多个统计和聚合一起分析。

说明 多个统计聚合如果复杂度较高可能会影响响应速度。

Java示例如下:

public void multipleGroupBy(SyncClient client) {
    // 构建查询语句
    SearchRequest searchRequest = SearchRequest.newBuilder()
        .tableName("tableName")
        .indexName("indexName")
        .searchQuery(
            SearchQuery.newBuilder()
                .query(QueryBuilders.matchAll())
                .limit(0)
                .addAggregation(AggregationBuilders.min("name1", "long"))
                .addAggregation(AggregationBuilders.sum("name2", "long"))
                .addAggregation(AggregationBuilders.distinctCount("name3", "long"))
                .addGroupBy(GroupByBuilders.groupByField("name4", "type"))
                .addGroupBy(GroupByBuilders.groupByRange("name5", "long").addRange(1, 15))
                .build())
        .build();
    //执行查询
    SearchResponse resp = client.search(searchRequest);
    //获取第1个统计聚合的结果
    System.out.println(resp.getAggregationResults().getAsMinAggregationResult("name1").getValue());
    //获取第2个统计聚合的结果
    System.out.println(resp.getAggregationResults().getAsSumAggregationResult("name2").getValue());
    //获取第3个统计聚合的结果
    System.out.println(resp.getAggregationResults().getAsDistinctCountAggregationResult("name3").getValue());
    //获取第4个统计聚合的结果
    for (GroupByFieldResultItem item : resp.getGroupByResults().getAsGroupByFieldResult("name4").getGroupByFieldResultItems()) {
        // key
        System.out.println(item.getKey());
        // 个数
        System.out.println(item.getRowCount());
    }
    //获取第5个统计聚合的结果
    for (GroupByRangeResultItem item : resp.getGroupByResults().getAsGroupByRangeResult("name4").getGroupByRangeResultItems()) {
        // 个数
        System.out.println(item.getRowCount());
    }
}

嵌套

GroupBy类型的聚合支持嵌套,其内部可以添加Aggregation和GroupBy类型的子统计聚合。GroupBy原本可以一直嵌套下去,为了性能、复杂度等综合考虑,只开放了一定的层数。

嵌套类型的统计聚合使用的场景主要是在分组内再次进行统计聚合,以两层的嵌套为例:
  • GroupBy+SubGroupBy:按省份分组后再按照城市分组,获得每个省份下每个城市的数据。
  • GroupBy+SubAggregation:按照省份分组后再求某一个指标的最大值,获得每一个省的某个指标最大值。

Java示例如下:

/**
 * 嵌套的统计聚合示例:外层2个Aggregation和1个GroupByField,GroupByField中又添加了2个Aggregation和1个GroupByRange
 */
public void subGroupBy(SyncClient client) {
    // 构建查询语句
    SearchRequest searchRequest = SearchRequest.newBuilder()
        .indexName("index_name")
        .tableName("table_name")
        .returnAllColumns(true)
        .searchQuery(
            SearchQuery.newBuilder()
                .query(QueryBuilders.match("textField", "hello"))
                .limit(10)
                .addAggregation(AggregationBuilders.min("name1", "fieldName1"))
                .addAggregation(AggregationBuilders.max("name2", "fieldName2"))
                .addGroupBy(GroupByBuilders
                    .groupByField("name3", "fieldName3")
                    .addSubAggregation(AggregationBuilders.max("subName1", "fieldName4"))
                    .addSubAggregation(AggregationBuilders.sum("subName2", "fieldName5"))
                    .addSubGroupBy(GroupByBuilders
                        .groupByRange("subName3", "fieldName6")
                        .addRange(12, 90)
                        .addRange(100, 900)
                    ))
                .build())
        .build();
    //执行查询
    SearchResponse resp = client.search(searchRequest);
    //第一层的agg结果
    AggregationResults aggResults = resp.getAggregationResults();
    System.out.println(aggResults.getAsMinAggregationResult("name1").getValue());
    System.out.println(aggResults.getAsMaxAggregationResult("name2").getValue());

    //取出第一层的groupByField结果,并同时取出其嵌套的agg结果
    GroupByFieldResult results = resp.getGroupByResults().getAsGroupByFieldResult("someName1");
    for (GroupByFieldResultItem item : results.getGroupByFieldResultItems()) {
        System.out.println("数量:" + item.getRowCount());
        System.out.println("key:" + item.getKey());

        //取出sub统计聚合的结果
        //SubAggregation: min 的结果
        System.out.println(item.getSubAggregationResults().getAsMaxAggregationResult("subName1"));
        //SubAggregation: max 的结果
        System.out.println(item.getSubAggregationResults().getAsSumAggregationResult("subName2"));
        //SubGroupBy: GroupByRange 的结果
        GroupByRangeResult subResults = resp.getGroupByResults().getAsGroupByRangeResult("subName3");
        for (GroupByRangeResultItem subItem : subResults.getGroupByRangeResultItems()) {
            System.out.println("数量:" + subItem.getRowCount());
            System.out.println("key:" + subItem.getKey());
        }
    }
}