Skip to content

Commit bfb935d

Browse files
fix: allow boolean numeric values in detected labels (#16997)
1 parent c48209c commit bfb935d

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

‎pkg/querier/querier.go

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ func countLabelsAndCardinality(storeLabelsMap map[string][]string, ingesterLabel
821821

822822
if ingesterLabels != nil {
823823
for label, val := range ingesterLabels.Labels {
824-
if _, isStatic := staticLabels[label]; (isStatic && val.Values != nil) || !containsAllIDTypes(val.Values) {
824+
if _, isStatic := staticLabels[label]; (isStatic && val.Values != nil) || !shouldRejectLabel(val.Values) {
825825
_, ok := dlMap[label]
826826
if !ok {
827827
dlMap[label] = newParsedLabels()
@@ -836,7 +836,7 @@ func countLabelsAndCardinality(storeLabelsMap map[string][]string, ingesterLabel
836836
}
837837

838838
for label, values := range storeLabelsMap {
839-
if _, isStatic := staticLabels[label]; (isStatic && values != nil) || !containsAllIDTypes(values) {
839+
if _, isStatic := staticLabels[label]; (isStatic && values != nil) || !shouldRejectLabel(values) {
840840
_, ok := dlMap[label]
841841
if !ok {
842842
dlMap[label] = newParsedLabels()
@@ -877,7 +877,38 @@ func (q *SingleTenantQuerier) Patterns(ctx context.Context, req *logproto.QueryP
877877
return res, err
878878
}
879879

880-
// containsAllIDTypes filters out all UUID, GUID and numeric types. Returns false if even one value is not of the type
880+
// shouldRejectLabel checks if the label should be rejected based on the values.
881+
// Returns true if all values are IDs or there are no values - meaning the label should be filtered out.
882+
// Special numeric case: boolean values (0,1) are not considered ID types.
883+
// Otherwise it returns false.
884+
func shouldRejectLabel(values []string) bool {
885+
// No values means we don't want to keep this label
886+
if len(values) == 0 {
887+
return true
888+
}
889+
890+
if len(values) > 2 {
891+
return containsAllIDTypes(values)
892+
}
893+
894+
// Boolean values should not be considered ID types
895+
boolValues := map[string]bool{
896+
"0": true,
897+
"1": true,
898+
}
899+
900+
for _, v := range values {
901+
if !boolValues[v] {
902+
return containsAllIDTypes(values)
903+
}
904+
}
905+
906+
return false
907+
}
908+
909+
// contjainsAllIDTypes checks if all values in the array are IDs (UUIDs or numeric values)
910+
// Returns false if at least one value is not an ID type - meaning the label should be kept.
911+
// Returns true if all values are IDs - meaning the label should be filtered out.
881912
func containsAllIDTypes(values []string) bool {
882913
for _, v := range values {
883914
_, err := strconv.ParseFloat(v, 64)

‎pkg/querier/querier_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,46 @@ func TestQuerier_DetectedLabels(t *testing.T) {
14141414
assert.Len(t, detectedLabels, 0)
14151415
})
14161416

1417+
t.Run("allows boolean values, even if numeric", func(t *testing.T) {
1418+
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{
1419+
"boolean-ints": {Values: []string{"0", "1"}},
1420+
"boolean-bools": {Values: []string{"true", "false"}},
1421+
"boolean-bools-uppercase": {Values: []string{"TRUE", "FALSE"}},
1422+
"single-id": {Values: []string{"751e8ee6-b377-4b2e-b7b5-5508fbe980ef"}},
1423+
"non-boolean-ints": {Values: []string{"6", "7"}},
1424+
}}
1425+
1426+
ingesterClient := newQuerierClientMock()
1427+
storeClient := newStoreMock()
1428+
1429+
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
1430+
Return(&ingesterResponse, nil)
1431+
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
1432+
Return([]string{}, nil)
1433+
1434+
querier, err := newQuerier(
1435+
conf,
1436+
mockIngesterClientConfig(),
1437+
newIngesterClientMockFactory(ingesterClient),
1438+
mockReadRingWithOneActiveIngester(),
1439+
&mockDeleteGettter{},
1440+
storeClient, limits)
1441+
require.NoError(t, err)
1442+
1443+
resp, err := querier.DetectedLabels(ctx, &request)
1444+
require.NoError(t, err)
1445+
1446+
detectedLabels := resp.DetectedLabels
1447+
assert.Len(t, detectedLabels, 3)
1448+
1449+
foundLabels := make([]string, 0, len(detectedLabels))
1450+
for _, d := range detectedLabels {
1451+
foundLabels = append(foundLabels, d.Label)
1452+
}
1453+
1454+
assert.ElementsMatch(t, []string{"boolean-ints", "boolean-bools", "boolean-bools-uppercase"}, foundLabels)
1455+
})
1456+
14171457
t.Run("static labels are always returned no matter their cardinality or value types", func(t *testing.T) {
14181458
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{
14191459
"cluster": {Values: []string{"val1"}},

0 commit comments

Comments
 (0)