fix: allow boolean numeric values in detected labels (#16997)

pull/17066/head
Trevor Whitney 2 months ago committed by GitHub
parent c48209c00c
commit bfb935d893
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 37
      pkg/querier/querier.go
  2. 40
      pkg/querier/querier_test.go

@ -821,7 +821,7 @@ func countLabelsAndCardinality(storeLabelsMap map[string][]string, ingesterLabel
if ingesterLabels != nil {
for label, val := range ingesterLabels.Labels {
if _, isStatic := staticLabels[label]; (isStatic && val.Values != nil) || !containsAllIDTypes(val.Values) {
if _, isStatic := staticLabels[label]; (isStatic && val.Values != nil) || !shouldRejectLabel(val.Values) {
_, ok := dlMap[label]
if !ok {
dlMap[label] = newParsedLabels()
@ -836,7 +836,7 @@ func countLabelsAndCardinality(storeLabelsMap map[string][]string, ingesterLabel
}
for label, values := range storeLabelsMap {
if _, isStatic := staticLabels[label]; (isStatic && values != nil) || !containsAllIDTypes(values) {
if _, isStatic := staticLabels[label]; (isStatic && values != nil) || !shouldRejectLabel(values) {
_, ok := dlMap[label]
if !ok {
dlMap[label] = newParsedLabels()
@ -877,7 +877,38 @@ func (q *SingleTenantQuerier) Patterns(ctx context.Context, req *logproto.QueryP
return res, err
}
// containsAllIDTypes filters out all UUID, GUID and numeric types. Returns false if even one value is not of the type
// shouldRejectLabel checks if the label should be rejected based on the values.
// Returns true if all values are IDs or there are no values - meaning the label should be filtered out.
// Special numeric case: boolean values (0,1) are not considered ID types.
// Otherwise it returns false.
func shouldRejectLabel(values []string) bool {
// No values means we don't want to keep this label
if len(values) == 0 {
return true
}
if len(values) > 2 {
return containsAllIDTypes(values)
}
// Boolean values should not be considered ID types
boolValues := map[string]bool{
"0": true,
"1": true,
}
for _, v := range values {
if !boolValues[v] {
return containsAllIDTypes(values)
}
}
return false
}
// contjainsAllIDTypes checks if all values in the array are IDs (UUIDs or numeric values)
// Returns false if at least one value is not an ID type - meaning the label should be kept.
// Returns true if all values are IDs - meaning the label should be filtered out.
func containsAllIDTypes(values []string) bool {
for _, v := range values {
_, err := strconv.ParseFloat(v, 64)

@ -1414,6 +1414,46 @@ func TestQuerier_DetectedLabels(t *testing.T) {
assert.Len(t, detectedLabels, 0)
})
t.Run("allows boolean values, even if numeric", func(t *testing.T) {
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{
"boolean-ints": {Values: []string{"0", "1"}},
"boolean-bools": {Values: []string{"true", "false"}},
"boolean-bools-uppercase": {Values: []string{"TRUE", "FALSE"}},
"single-id": {Values: []string{"751e8ee6-b377-4b2e-b7b5-5508fbe980ef"}},
"non-boolean-ints": {Values: []string{"6", "7"}},
}}
ingesterClient := newQuerierClientMock()
storeClient := newStoreMock()
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(&ingesterResponse, nil)
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return([]string{}, nil)
querier, err := newQuerier(
conf,
mockIngesterClientConfig(),
newIngesterClientMockFactory(ingesterClient),
mockReadRingWithOneActiveIngester(),
&mockDeleteGettter{},
storeClient, limits)
require.NoError(t, err)
resp, err := querier.DetectedLabels(ctx, &request)
require.NoError(t, err)
detectedLabels := resp.DetectedLabels
assert.Len(t, detectedLabels, 3)
foundLabels := make([]string, 0, len(detectedLabels))
for _, d := range detectedLabels {
foundLabels = append(foundLabels, d.Label)
}
assert.ElementsMatch(t, []string{"boolean-ints", "boolean-bools", "boolean-bools-uppercase"}, foundLabels)
})
t.Run("static labels are always returned no matter their cardinality or value types", func(t *testing.T) {
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{
"cluster": {Values: []string{"val1"}},

Loading…
Cancel
Save