Es聚合查询

496 阅读2分钟

Es聚合查询

使用es查询数据的时候不可避免需要按照某些字段聚合,下面是一些简单示例,不废话,直接写代码。

代码使用golang,库github.com/olivere/elastic/v7 v7.0.32

1.聚合查询示例

 func aggrTest(ctx context.Context, req QueryReq) (res []*Res, err error) {
   res = make([]*Res, 0)
   aliases := ["index"] // 这里可以放需要的索引和别名
   st, et := req.StartTime*1000, req.EndTime*1000
​
   searchService := component.GetESClient().Search().Index(aliases...).Size(0)
   //通过req 构建查询条件 如果没有忽略
   searchService = searchService.Query(query)
   
   // 创建复合聚合 首先按照时间 1m 聚合 在同一时间下按照code 对sent进行sum聚合 如果聚合条件多,可以多几个 SubAggregation
   aggregation := elastic.NewDateHistogramAggregation().
        FixedInterval("1m").TimeZone(time.UTC.String()).
        Field("时间字段").MinDocCount(0).
        SubAggregation("codeGroup", elastic.NewTermsAggregation().Field("code").Size(1000).
            SubAggregation("total", elastic.NewSumAggregation().Field("sent"))).
        ExtendedBounds(st, et).OrderByKeyAsc()
​
    searchService = searchService.Aggregation("dateTimeRes", aggregation).Size(0)
    searchResult, err := searchService.Do(ctx)
    if err != nil {
        log.Error(ctx, err)
        return res, consts.ErrInternalError
    }
    if dateTimeBucket, b := searchResult.Aggregations.DateHistogram("dateTimeRes"); b {
        for _, item := range dateTimeBucket.Buckets {
            tmp := new(CodeValue)
            tmp.TimeStamp = int64(item.Key) / 1000
            tmp.Items = []CodeItem{}
​
            byCodeAgg, found := item.Aggregations.Terms("codeGroup")
            if !found {
                continue
            }
​
            for _, codeBucket := range byCodeAgg.Buckets {
                if tCount, ok := codeBucket.Aggregations.Sum("total"); ok {
                    item := model.CodeItem{}
                    item.Code = int(codeBucket.Key.(float64))
                    item.Count = int64(*tCount.Value)
                    tmp.Items = append(codeValue.Items, item)
                }
            }
            res = append(res, tmp)
        }
    }
     return
}

2.优化

在上面的查询中,如果聚合的字段比较多,或者聚合出来的数据条目很多,会导致查询出来的数据不完整。

为了解决以上问题,需要用到es 的 Composite来聚合

func CompositeTest(ctx context.Context, req QueryReq) (res []*Res, err error) {
    res = make([]*Res, 0)
    aliases := ["index"] // 这里可以放需要的索引和别名
    st, et := req.StartTime*1000, req.EndTime*1000
​
    searchService := component.GetESClient().Search().Index(aliases...).Size(0)
    //通过req 构建查询条件 如果没有忽略
    searchService = searchService.Query(query)
​
    // 创建复合聚合 按照时间每分钟,对 省份,运营商 下用户访问次数(http,https)进行聚合
    // NewCompositeAggregation的size是一次查询结果的条目
    compositeAgg := elastic.NewCompositeAggregation().Size(es.EsDefaultSize).
        Sources(
            elastic.NewCompositeAggregationDateHistogramValuesSource("DateTimeRes").
                FixedInterval("1m").
                Field(criteria[0].Field).TimeZone(time.UTC.String()),
            elastic.NewCompositeAggregationTermsValuesSource("ispRes").Field("isp"),
            elastic.NewCompositeAggregationTermsValuesSource("provinceRes").Field("province"),
        ).SubAggregation("hCountRes", elastic.NewSumAggregation().Field("hCount")).
        SubAggregation("hsCountRes", elastic.NewSumAggregation().Field("hsCount"))
    var afterKey map[string]interface{}
    for {
        if afterKey != nil {
            compositeAgg.AggregateAfter(afterKey)
        }
        searchResult, err := searchService.Aggregation("CompositeRes", compositeAgg).Do(ctx)
        if err != nil {
            log.Error(ctx, err)
            return res, err
        }
        compositeResult, b := searchResult.Aggregations.Composite("CompositeRes")
        if !b {
            log.Errorf(ctx, "composite data err:%+v", err)
            return res, err
        }
        for _, bucket := range compositeResult.Buckets {
            tmp := new(Res)
            if ts, ok := bucket.Key["DateTimeRes"].(float64); ok {
                tmp.TimeStamp = int64(ts) / 1000
            }
            if isp, ok := bucket.Key["ispRes"].(string); ok {
                tmp.Isp = isp
            }
            if province, ok := bucket.Key["provinceRes"].(string); ok {
                tmp.Province = province
            }
            if c, ok := bucket.Aggregations.Sum("hCountRes"); ok {
                tmp.HttpCount = int64(*c.Value)
            } 
            if c, ok := bucket.Aggregations.Sum("hsCountRes"); ok {
                tmp.HttpsCount = int64(*c.Value)
            }
            res = append(res, tmp)
        }
​
        // 如果没有更多的桶,结束循环
        if compositeResult.AfterKey == nil {
            break
        }
        // 更新 afterKey 以获取下一个批次的桶
        afterKey = compositeResult.AfterKey
    }
   return res, nil
}