Files
grafana/pkg/tsdb/elasticsearch/field_namer.go
T
Andrew Hackmann a327fec8df Elasticsearch: data query refactor (#113360)
* split up data_query.go

* split up response_parser

* remove white space
2025-11-19 15:10:12 +00:00

171 lines
4.4 KiB
Go

package elasticsearch
import (
"sort"
"strings"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
)
// nameFields applies naming logic to data frame fields based on query configuration
func nameFields(queryResult backend.DataResponse, target *Query, keepLabelsInResponse bool) {
set := make(map[string]struct{})
frames := queryResult.Frames
for _, v := range frames {
for _, vv := range v.Fields {
if metricType, exists := vv.Labels["metric"]; exists {
if _, ok := set[metricType]; !ok {
set[metricType] = struct{}{}
}
}
}
}
metricTypeCount := len(set)
for _, frame := range frames {
if frame.Meta != nil && frame.Meta.Type == data.FrameTypeTimeSeriesMulti {
// if it is a time-series-multi, it means it has two columns, one is "time",
// another is "number"
valueField := frame.Fields[1]
fieldName := getFieldName(*valueField, target, metricTypeCount)
// If we need to keep the labels in the response, to prevent duplication in names and to keep
// backward compatibility with alerting and expressions we use DisplayNameFromDS
if keepLabelsInResponse {
if valueField.Config == nil {
valueField.Config = &data.FieldConfig{}
}
valueField.Config.DisplayNameFromDS = fieldName
// If we don't need to keep labels (how frontend mode worked), we use frame.Name and remove labels
} else {
valueField.Labels = nil
frame.Name = fieldName
}
}
}
}
// getFieldName generates a field name based on the data field and target configuration
func getFieldName(dataField data.Field, target *Query, metricTypeCount int) string {
metricType := dataField.Labels["metric"]
metricName := getMetricName(metricType)
delete(dataField.Labels, "metric")
field := ""
if v, ok := dataField.Labels["field"]; ok {
field = v
delete(dataField.Labels, "field")
}
if target.Alias != "" {
frameName := target.Alias
subMatches := aliasPatternRegex.FindAllStringSubmatch(target.Alias, -1)
for _, subMatch := range subMatches {
group := subMatch[0]
if len(subMatch) > 1 {
group = subMatch[1]
}
if strings.Index(group, "term ") == 0 {
frameName = strings.Replace(frameName, subMatch[0], dataField.Labels[group[5:]], 1)
}
if v, ok := dataField.Labels[group]; ok {
frameName = strings.Replace(frameName, subMatch[0], v, 1)
}
if group == "metric" {
frameName = strings.Replace(frameName, subMatch[0], metricName, 1)
}
if group == "field" {
frameName = strings.Replace(frameName, subMatch[0], field, 1)
}
}
return frameName
}
// todo, if field and pipelineAgg
if isPipelineAgg(metricType) {
if metricType != "" && isPipelineAggWithMultipleBucketPaths(metricType) {
metricID := ""
if v, ok := dataField.Labels["metricId"]; ok {
metricID = v
}
for _, metric := range target.Metrics {
if metric.ID == metricID {
metricName = metric.Settings.Get("script").MustString()
for name, pipelineAgg := range metric.PipelineVariables {
for _, m := range target.Metrics {
if m.ID == pipelineAgg {
metricName = strings.ReplaceAll(metricName, "params."+name, describeMetric(m.Type, m.Field))
}
}
}
}
}
} else {
if field != "" {
found := false
for _, metric := range target.Metrics {
if metric.ID == field {
metricName += " " + describeMetric(metric.Type, metric.Field)
found = true
}
}
if !found {
metricName = "Unset"
}
}
}
} else if field != "" {
metricName += " " + field
}
delete(dataField.Labels, "metricId")
if len(dataField.Labels) == 0 {
return metricName
}
name := ""
for _, v := range getSortedLabelValues(dataField.Labels) {
name += v + " "
}
if metricTypeCount == 1 {
return strings.TrimSpace(name)
}
return strings.TrimSpace(name) + " " + metricName
}
// getSortedLabelValues sorts label keys and returns the label values in sorted order
func getSortedLabelValues(labels data.Labels) []string {
keys := make([]string, 0, len(labels))
for key := range labels {
keys = append(keys, key)
}
sort.Strings(keys)
values := make([]string, len(keys))
for i, key := range keys {
values[i] = labels[key]
}
return values
}
// getMetricName returns the display name for a metric type
func getMetricName(metric string) string {
if text, ok := metricAggType[metric]; ok {
return text
}
if text, ok := extendedStats[metric]; ok {
return text
}
return metric
}