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
}