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

类型

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

        统计每个类别的商品数量,且统计每个类别商品价格的最大值和最小值。

      • 方法

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

      • 结果示例
        • 水果:5个(其中价格最高15元,最低3元)
        • 洗漱用品:10个(其中价格最高98元,最低1元)
        • 电子设备:3个(其中价格最高8699元,最低2300元)
        • 其它:15个(其中价格最高1000元,最低80元)
    • 示例
      /**
       * 所有商品中每一个类别各有多少个?且统计每一个类别的价格最大值和最小值。
       * 返回结果举例:"水果:5个(其中价格最贵15元,最便宜3元),洗漱用品:10个(其中价格最贵98元,最便宜1元),电子设备:3个(其中价格最贵8699元,最便宜2300元),其它:15个(其中价格最贵1000
       * 元,最便宜80元)"
       */
      func GroupByField(client *tablestore.TableStoreClient, tableName string, indexName string) {
          searchRequest := &tablestore.SearchRequest{}
      
          searchRequest.
              SetTableName(tableName).    //设置表名
              SetIndexName(indexName).    //设置多元索引名
              SetSearchQuery(search.NewSearchQuery().
                  SetQuery(&search.MatchAllQuery{}).    //匹配所有行
                  SetLimit(0).
                  GroupBy(search.NewGroupByField("group1", "column_type").    //按商品类别的字段值进行分组
                      SubAggregation(search.NewMinAggregation("min_price", "column_price")).    //分组中最便宜的商品
                      SubAggregation(search.NewMaxAggregation("max_price", "column_price"))))    //分组中最贵的商品
      
          searchResponse, err := client.Search(searchRequest)
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
          group, err := groupByResults.GroupByField("group1")
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
      
          for _, item := range group.Items {    //遍历返回的所有分组
              //分组的值和分组中的记录行数
              fmt.Println("\tkey: ", item.Key, ", rowCount: ", item.RowCount)    //打印本次分组的行数
      
              //最低价格
              minPrice, _ := item.SubAggregations.Min("min_price")
              if minPrice.HasValue() {
                  fmt.Println("\t\tmin_price: ", minPrice.Value)
              }
      
              //最高价格
              maxPrice, _ := item.SubAggregations.Max("max_price")
              if maxPrice.HasValue() {
                  fmt.Println("\t\tmax_price: ", maxPrice.Value)
              }
          }
      }
  • 数值范围分组
    • 定义:在查询结果上根据一个字段的范围进行分组,字段值在某范围内则会放到一个分组内,最终返回每一个范围中相应的item个数。例如求商品销量时候按这些分组计算每个范围的销量:[0,1k)、[1k,5k)、[5k,正无穷)。
    • 参数说明
      参数 说明
      Name 用户给本次统计聚合功能自定义的名字,用于区分不同的统计聚合操作。在使用结果时,需要根据该名字找到本次统计聚合的结果。
      FieldName 聚合作用的字段,仅支持long、double类型。
      Range(fromInclusive float64, toExclusive float64) 添加分组的范围。起始值fromInclusive可以使用最小值NegInf,结束值toExclusive可以使用最大值Inf。
      SubAggregation和SubGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。例如:按销量分组后再按省份分组,即可获得某个销量段内哪个省比重比较大,其实现是GroupByRange内添加一个GroupByField。
    • 示例
      /**
       * 求商品销量时候按[0,1k)、[1k,5k)、[5k,正无穷)进行分组计算每个范围的销量。
       */
      func GroupByRange(client *tablestore.TableStoreClient, tableName string, indexName string) {
          searchRequest := &tablestore.SearchRequest{}
      
          searchRequest.
              SetTableName(tableName).    //设置表名
              SetIndexName(indexName).    //设置多元索引名
              SetSearchQuery(search.NewSearchQuery().
                  SetQuery(&search.MatchAllQuery{}).    //匹配所有行
                  SetLimit(0).
                  GroupBy(search.NewGroupByRange("group1", "column_number").
                      Range(search.NegInf, 1000).
                      Range(1000, 5000).
                      Range(5000, search.Inf)))
      
          searchResponse, err := client.Search(searchRequest)
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
          group, err := groupByResults.GroupByRange("group1")
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          for _, item := range group.Items {    //遍历返回的所有分组
              fmt.Println("\t[", item.From, ", ", item.To, "), rowCount: ", item.RowCount)    //打印本次分组的行数
          }
      }
  • 地理距离分组
    • 定义:在查询结果上根据距离某一个中心点的范围进行分组,距离差值在某范围内则会放到一个分组内,最终返回每一个范围中相应的item个数。例如求距离万达广场这些范围内的人数:[0,1000米)、[1000米,5000米)、[5000米,正无穷)。
    • 参数说明
      参数 说明
      Name 用户给本次统计聚合功能自定义的名字,用于区分不同的统计聚合操作。在使用结果时,需要根据该名字找到本次统计聚合的结果。
      FieldName 聚合作用的字段,仅支持geo_point类型。
      CenterPoint(latitude float64, longitude float64) latitude是起始中心点坐标纬度,longitude是起始中心点坐标经度。
      Range(fromInclusive float64, toExclusive float64) 添加分组的范围,单位为米。起始值fromInclusive可以使用最小值NegInf,结束值toExclusive可以使用最大值Inf。
      SubAggregation和SubGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。
    • 示例
      
      /**
       * 求距离万达广场[0,1000米)、[1000米,5000米)、[5000米,正无穷)每个范围的人数。
       */
      func GroupByGeoDistance(client *tablestore.TableStoreClient, tableName string, indexName string) {
          searchRequest := &tablestore.SearchRequest{}
      
          searchRequest.
              SetTableName(tableName).    //设置表名
              SetIndexName(indexName).    //设置多元索引名
              SetSearchQuery(search.NewSearchQuery().
                  SetQuery(&search.MatchAllQuery{}).    //匹配所有行
                  SetLimit(0).
                  GroupBy(search.NewGroupByGeoDistance("group1", "Col_GeoPoint", search.GeoPoint{Lat: 30.137817, Lon:120.08681}).
                      Range(search.NegInf, 1000).
                      Range(1000, 5000).
                      Range(5000, search.Inf)))
      
          searchResponse, err := client.Search(searchRequest)
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
          group, err := groupByResults.GroupByGeoDistance("group1")
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          for _, item := range group.Items {    //遍历返回的所有分组
              fmt.Println("\t[", item.From, ", ", item.To, "), rowCount: ", item.RowCount)    //打印本次分组的行数
          }
      }
  • Filter分组
    • 定义:在查询结果上,按照Filter(即Query)进行分组,然后获得每个Filter匹配到的数量。返回的结果顺序和添加的Filter顺序一致。例如添加三个Filter(销量大于100、产地是浙江、描述中包含浙江关键词),然后获得这三个Filter匹配到的数量。
    • 参数说明
      参数 说明
      Name 用户给本次统计聚合功能命名的名字,用于区分不同的统计聚合操作。在使用结果时候,需要根据该名字找到本次统计聚合的结果。
      Query 添加的Filter(Query)。
      SubAggregation和SubGroupBy 添加子统计聚合。子统计聚合会根据分组内容进行再一次的统计聚合分析。
    • 示例
      /**
       * 按照Filter(即Query)进行分组,例如添加三个Filter(销量大于100、产地是浙江、描述中包含浙江关键词),然后获得每个Filter匹配到的数量。
       */
      func GroupByFilter(client *tablestore.TableStoreClient, tableName string, indexName string) {
          searchRequest := &tablestore.SearchRequest{}
      
          searchRequest.
              SetTableName(tableName).    //设置表名
              SetIndexName(indexName).    //设置多元索引名
              SetSearchQuery(search.NewSearchQuery().
                  SetQuery(&search.MatchAllQuery{}).    //匹配所有行
                  SetLimit(0).
                  GroupBy(search.NewGroupByFilter("group1").
                      Query(&search.RangeQuery{
                          FieldName: "number",
                          From: 100,
                          IncludeLower: true}).
                      Query(&search.TermQuery{
                          FieldName: "place",
                          Term:      "浙江省",
                      }).
                      Query(&search.MatchQuery{
                          FieldName: "description",
                          Text: "浙江",
                      })))
      
          searchResponse, err := client.Search(searchRequest)
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
          group, err := groupByResults.GroupByFilter("group1")
          if err != nil {
              fmt.Printf("%#v", err)
              return
          }
          for _, item := range group.Items {    //遍历返回的所有分组
              fmt.Println("\trowCount: ", item.RowCount)    //打印本次分组的行数
          }
      }

多个统计聚合

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

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

示例

func MultipleAggregationsAndGroupBysSample(client *tablestore.TableStoreClient, tableName string, indexName string) {
    searchRequest := &tablestore.SearchRequest{}

    searchRequest.
        SetTableName(tableName).    //设置表名
        SetIndexName(indexName).    //设置多元索引名
        SetSearchQuery(search.NewSearchQuery().
            SetQuery(&search.MatchAllQuery{}).    //匹配所有行
            SetLimit(0).
            Aggregation(search.NewAvgAggregation("agg1", "Col_Long")).                //计算Col_Long字段的平均值
            Aggregation(search.NewDistinctCountAggregation("agg2", "Col_Long")).    //计算Col_Long字段不同取值的个数
            Aggregation(search.NewMaxAggregation("agg3", "Col_Long")).                //计算Col_Long字段的最大值
            GroupBy(search.NewGroupByField("group1", "Col_Keyword").    //对Col_Keyword字段做GroupByField取值聚合
                GroupBySorters([]search.GroupBySorter{}).    //可以指定返回结果分组的顺序
                Size(2).                                //仅返回前2个分组
                SubAggregation(search.NewAvgAggregation("sub_agg1", "Col_Long")).    //对每个分组进行子统计(Aggregation)
                SubGroupBy(search.NewGroupByField("sub_group1", "Col_Keyword2"))).    //对每个分组进行子聚合(GroupBy)
            GroupBy(search.NewGroupByRange("group2", "Col_Long").        //对Col_Long字段做GroupByRange范围
                Range(search.NegInf, 3).            //第一个分组包含Col_Long在(-∞, 3)的索引行
                Range(3, 5).            //第二个分组包含Col_Long在[3, 5)的索引行
                Range(5, search.Inf)))            //第三个分组包含Col_Long在[5, +∞)的索引行

    // 设置返回所有列
    searchResponse, err := client.Search(searchRequest)
    if err != nil {
        fmt.Printf("%#v", err)
        return
    }

    aggResults := searchResponse.AggregationResults    //获取所有统计结果
    //agg1
    agg1, err := aggResults.Avg("agg1")        //获取名字为"agg1"的Aggregation结果,类型为Avg
    if err != nil {
        panic(err)
    }
    if agg1.HasValue() {                            //名字为"agg1"的Aggregation结果中,是否存在“Value”的值
        fmt.Println("(avg) agg1: ", agg1.Value)    //打印Col_Long字段平均值
    } else {
        fmt.Println("(avg) agg1: no value")        //所有行都不存在Col_Long字段
    }

    //agg2
    agg2, err := aggResults.DistinctCount("agg2")    //获取名字为"agg2"的Aggregation结果,类型为DistinctCount
    if err != nil {
        panic(err)
    }
    fmt.Println("(distinct) agg2: ", agg2.Value)        //打印Col_Long字段不同取值的个数

    //agg3
    agg3, err := aggResults.Max("agg3")        //获取名字为"agg3"的Aggregation结果,类型为Max
    if err != nil {
        panic(err)
    }
    if agg3.HasValue() {
        fmt.Println("(max) agg3: ", agg3.Value)    //打印Col_Long字段最大值
    } else {
        fmt.Println("(max) agg3: no value")        //所有行都不存在Col_Long字段
    }

    groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
    //group1
    group1, err := groupByResults.GroupByField("group1")    //获取名字为"group1"的GroupBy结果,类型为GroupByField
    if err != nil {
        panic(err)
    }
    fmt.Println("group1: ")
    for _, item := range group1.Items {    //遍历返回的所有分组
        //item
        fmt.Println("\tkey: ", item.Key, ", rowCount: ", item.RowCount)    //打印本次分组的行数

        //sub agg
        subAgg1, err := item.SubAggregations.Avg("sub_agg1")    //获取名字为sub_agg1的子统计的结果
        if err != nil {
            panic(err)
        }
        if subAgg1.HasValue() {    //如果子统计sub_agg1计算出了Col_Long字段的平均值,则HasValue()返回true
            fmt.Println("\t\tsub_agg1: ", subAgg1.Value)    //打印本次分组中,子统计计算出来的Col_Long字段的平均值
        }

        //sub group by
        subGroup1, err := item.SubGroupBys.GroupByField("sub_group1")    //获取名字为sub_group1的子聚合的结果
        if err != nil {
            panic(err)
        }
        fmt.Println("\t\tsub_group1")
        for _, subItem := range subGroup1.Items {    //遍历名字为sub_group1的子聚合结果
            fmt.Println("\t\t\tkey: ", subItem.Key, ", rowCount: ", subItem.RowCount)    //打印sub_group1子聚合的结果分组,即分组中的行数
            tablestore.Assert(subItem.SubAggregations.Empty(), "")
            tablestore.Assert(subItem.SubGroupBys.Empty(), "")
        }
    }

    //group2
    group2, err := groupByResults.GroupByRange("group2")    //获取名字为"group2"的GroupBy结果,类型为GroupByRange
    if err != nil {
        panic(err)
    }
    fmt.Println("group2: ")
    for _, item := range group2.Items {    //遍历返回的所有分组
        fmt.Println("\t[", item.From, ", ", item.To, "), rowCount: ", item.RowCount)    //打印本次分组的行数
    }
}

嵌套

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

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

示例

/**
 * 嵌套的统计聚合示例:外层GroupByField下添加了2个Aggregation和1个GroupByRange
 */
func NestedSample(client *tablestore.TableStoreClient, tableName string, indexName string) {
    searchRequest := &tablestore.SearchRequest{}

    searchRequest.
        SetTableName(tableName).    //设置表名
        SetIndexName(indexName).    //设置多元索引名
        SetSearchQuery(search.NewSearchQuery().
            SetQuery(&search.MatchAllQuery{}).    //匹配所有行
            SetLimit(0).
            GroupBy(search.NewGroupByField("group1", "field1").
                SubAggregation(search.NewMinAggregation("sub_agg1", "sub_field1")).
                SubAggregation(search.NewMaxAggregation("sub_agg2", "sub_field2")).
                SubGroupBy(search.NewGroupByRange("sub_group1", "sub_field3").
                    Range(search.NegInf, 3).
                    Range(3, 5).
                    Range(5, search.Inf))))

    searchResponse, err := client.Search(searchRequest)
    if err != nil {
        fmt.Printf("%#v", err)
        return
    }
    groupByResults := searchResponse.GroupByResults    //获取所有聚合结果
    group, err := groupByResults.GroupByField("group1")
    if err != nil {
        fmt.Printf("%#v", err)
        return
    }

    for _, item := range group.Items {    //遍历返回的所有分组
        //分组的值和分组中的记录行数
        fmt.Println("\tkey: ", item.Key, ", rowCount: ", item.RowCount)    //打印本次分组的行数

        //sub_agg1
        subAgg1, _ := item.SubAggregations.Min("sub_agg1")
        if subAgg1.HasValue() {
            fmt.Println("\t\tsub_agg1: ", subAgg1.Value)
        }

        //sub_agg2
        subAgg2, _ := item.SubAggregations.Max("sub_agg2")
        if subAgg2.HasValue() {
            fmt.Println("\t\tsub_agg2: ", subAgg2.Value)
        }

        //sub_group1
        subGroup, _ := item.SubGroupBys.GroupByRange("sub_group1")
        for _, item := range subGroup.Items {
            fmt.Println("\t\t[", item.From, ", ", item.To, "), rowCount: ", item.RowCount)
        }
    }
}