Skip to content

Commit 49a0ed9

Browse files
committed
equals vs contains
1 parent 6522b8e commit 49a0ed9

33 files changed

+561
-394
lines changed

pkg/loki/flow_query.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,13 @@ func (q *FlowQueryBuilder) addFilter(filter filters.Match) error {
114114
} else if q.config.IsIP(filter.Key) {
115115
q.addIPFilters(filter.Key, values, filter.Not)
116116
} else {
117-
q.addLineFilters(filter.Key, values, filter.Not, filter.MoreThanOrEqual)
117+
q.addLineFilters(filter.Key, values, filter.Not, filter.Equal, filter.MoreThanOrEqual)
118118
}
119119

120120
return nil
121121
}
122122

123-
func (q *FlowQueryBuilder) addLineFilters(key string, values []string, not bool, moreThan bool) {
123+
func (q *FlowQueryBuilder) addLineFilters(key string, values []string, not, equal, moreThan bool) {
124124
if len(values) == 0 {
125125
return
126126
}
@@ -132,6 +132,8 @@ func (q *FlowQueryBuilder) addLineFilters(key string, values []string, not bool,
132132
var hasEmptyMatch bool
133133
if q.config.IsNumeric(key) {
134134
lf, hasEmptyMatch = filters.NumericLineFilter(key, values, not, moreThan)
135+
} else if equal {
136+
lf, hasEmptyMatch = filters.StringLineFilter(key, values, not)
135137
} else {
136138
lf, hasEmptyMatch = filters.StringLineFilterCheckExact(key, values, not)
137139
}

pkg/model/filters/filters.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,25 @@ type SingleQuery = []Match
1414
type Match struct {
1515
Key string
1616
Values string
17+
Equal bool
1718
Not bool
1819
MoreThanOrEqual bool
1920
}
2021

21-
func NewMatch(key, values string) Match { return Match{Key: key, Values: values} }
22+
func NewMatch(key, values string) Match {
23+
return Match{Key: key, Values: values}
24+
}
25+
func NewEqual(key, values string) Match {
26+
return Match{Key: key, Values: values, Equal: true}
27+
}
2228
func NewNotMatch(key, values string) Match {
23-
return Match{Key: key, Values: values, Not: true, MoreThanOrEqual: false}
29+
return Match{Key: key, Values: values, Not: true}
2430
}
25-
func NewMoreThanOrEqualMatch(key, values string) Match {
26-
return Match{Key: key, Values: values, Not: false, MoreThanOrEqual: true}
31+
func NewNotEqual(key, values string) Match {
32+
return Match{Key: key, Values: values, Equal: true, Not: true}
33+
}
34+
func NewMoreThanOrEqual(key, values string) Match {
35+
return Match{Key: key, Values: values, MoreThanOrEqual: true}
2736
}
2837

2938
// Example of raw filters (url-encoded):
@@ -45,12 +54,21 @@ func Parse(raw string) (MultiQueries, error) {
4554
var andFilters []Match
4655
filters := strings.Split(group, "&")
4756
for _, filter := range filters {
48-
pair := strings.Split(filter, "=")
49-
if len(pair) == 2 {
57+
if strings.Contains(filter, "=") {
58+
pair := strings.Split(filter, "=")
59+
if len(pair) == 2 {
60+
if strings.HasSuffix(pair[0], "!") {
61+
andFilters = append(andFilters, NewNotEqual(strings.TrimSuffix(pair[0], "!"), pair[1]))
62+
} else if strings.HasSuffix(pair[0], ">") {
63+
andFilters = append(andFilters, NewMoreThanOrEqual(strings.TrimSuffix(pair[0], ">"), pair[1]))
64+
} else {
65+
andFilters = append(andFilters, NewEqual(pair[0], pair[1]))
66+
}
67+
}
68+
} else if strings.Contains(filter, "~") {
69+
pair := strings.Split(filter, "~")
5070
if strings.HasSuffix(pair[0], "!") {
5171
andFilters = append(andFilters, NewNotMatch(strings.TrimSuffix(pair[0], "!"), pair[1]))
52-
} else if strings.HasSuffix(pair[0], ">") {
53-
andFilters = append(andFilters, NewMoreThanOrEqualMatch(strings.TrimSuffix(pair[0], ">"), pair[1]))
5472
} else {
5573
andFilters = append(andFilters, NewMatch(pair[0], pair[1]))
5674
}
@@ -83,15 +101,15 @@ func (m MultiQueries) Distribute(toDistribute []SingleQuery, ignorePred func(Sin
83101

84102
func (m *Match) ToLabelFilter() (LabelFilter, bool) {
85103
values := strings.Split(m.Values, ",")
86-
if len(values) == 1 && isExactMatch(values[0]) {
104+
if len(values) == 1 && (m.Equal || isExactMatch(values[0])) {
87105
if m.Not {
88106
return NotStringLabelFilter(m.Key, trimExactMatch(values[0])), true
89107
} else if m.MoreThanOrEqual {
90108
return MoreThanNumberLabelFilter(m.Key, trimExactMatch(values[0])), true
91109
}
92110
return StringEqualLabelFilter(m.Key, trimExactMatch(values[0])), true
93111
}
94-
return MultiValuesRegexFilter(m.Key, values, m.Not)
112+
return MultiValuesRegexFilter(m.Key, values, m.Not, m.Equal)
95113
}
96114

97115
func isExactMatch(value string) bool {

pkg/model/filters/filters_test.go

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ import (
1010

1111
func TestParseFilters(t *testing.T) {
1212
// 2 groups
13-
groups, err := Parse(url.QueryEscape("foo=a,b&bar=c|baz=d"))
13+
groups, err := Parse(url.QueryEscape("foo~a,b&bar=c|baz=d"))
1414
require.NoError(t, err)
1515

1616
assert.Len(t, groups, 2)
1717
assert.Equal(t, SingleQuery{
1818
NewMatch("foo", "a,b"),
19-
NewMatch("bar", "c"),
19+
NewEqual("bar", "c"),
2020
}, groups[0])
2121
assert.Equal(t, SingleQuery{
22-
NewMatch("baz", "d"),
22+
NewEqual("baz", "d"),
2323
}, groups[1])
2424

2525
// Resource path + port, match all
@@ -28,10 +28,10 @@ func TestParseFilters(t *testing.T) {
2828

2929
assert.Len(t, groups, 1)
3030
assert.Equal(t, SingleQuery{
31-
NewMatch("SrcK8S_Type", `"Pod"`),
32-
NewMatch("SrcK8S_Namespace", `"default"`),
33-
NewMatch("SrcK8S_Name", `"test"`),
34-
NewMatch("SrcPort", "8080"),
31+
NewEqual("SrcK8S_Type", `"Pod"`),
32+
NewEqual("SrcK8S_Namespace", `"default"`),
33+
NewEqual("SrcK8S_Name", `"test"`),
34+
NewEqual("SrcPort", "8080"),
3535
}, groups[0])
3636

3737
// Resource path + port, match any
@@ -40,13 +40,13 @@ func TestParseFilters(t *testing.T) {
4040

4141
assert.Len(t, groups, 2)
4242
assert.Equal(t, SingleQuery{
43-
NewMatch("SrcK8S_Type", `"Pod"`),
44-
NewMatch("SrcK8S_Namespace", `"default"`),
45-
NewMatch("SrcK8S_Name", `"test"`),
43+
NewEqual("SrcK8S_Type", `"Pod"`),
44+
NewEqual("SrcK8S_Namespace", `"default"`),
45+
NewEqual("SrcK8S_Name", `"test"`),
4646
}, groups[0])
4747

4848
assert.Equal(t, SingleQuery{
49-
NewMatch("SrcPort", "8080"),
49+
NewEqual("SrcPort", "8080"),
5050
}, groups[1])
5151

5252
// Resource path + name, match all
@@ -55,10 +55,10 @@ func TestParseFilters(t *testing.T) {
5555

5656
assert.Len(t, groups, 1)
5757
assert.Equal(t, SingleQuery{
58-
NewMatch("SrcK8S_Type", `"Pod"`),
59-
NewMatch("SrcK8S_Namespace", `"default"`),
60-
NewMatch("SrcK8S_Name", `"test"`),
61-
NewMatch("SrcK8S_Name", `"nomatch"`),
58+
NewEqual("SrcK8S_Type", `"Pod"`),
59+
NewEqual("SrcK8S_Namespace", `"default"`),
60+
NewEqual("SrcK8S_Name", `"test"`),
61+
NewEqual("SrcK8S_Name", `"nomatch"`),
6262
}, groups[0])
6363
}
6464

@@ -68,45 +68,45 @@ func TestParseCommon(t *testing.T) {
6868

6969
assert.Len(t, groups, 2)
7070
assert.Equal(t, SingleQuery{
71-
NewMatch("srcns", "a"),
71+
NewEqual("srcns", "a"),
7272
}, groups[0])
7373
assert.Equal(t, SingleQuery{
74-
NewNotMatch("srcns", "a"),
75-
NewMatch("dstns", "a"),
74+
NewNotEqual("srcns", "a"),
75+
NewEqual("dstns", "a"),
7676
}, groups[1])
7777
}
7878

7979
func TestDistribute(t *testing.T) {
8080
mq := MultiQueries{
81-
SingleQuery{NewMatch("key1", "a"), NewMatch("key2", "b")},
82-
SingleQuery{NewMatch("key1", "AA"), NewMatch("key3", "CC")},
83-
SingleQuery{NewMatch("key-ignore", "ZZ")},
81+
SingleQuery{NewEqual("key1", "a"), NewEqual("key2", "b")},
82+
SingleQuery{NewEqual("key1", "AA"), NewEqual("key3", "CC")},
83+
SingleQuery{NewEqual("key-ignore", "ZZ")},
8484
}
85-
toDistribute := []SingleQuery{{NewMatch("key10", "XX")}, {NewMatch("key11", "YY")}}
85+
toDistribute := []SingleQuery{{NewEqual("key10", "XX")}, {NewEqual("key11", "YY")}}
8686
res := mq.Distribute(toDistribute, func(q SingleQuery) bool { return q[0].Key == "key-ignore" })
8787

8888
assert.Len(t, res, 5)
8989
assert.Equal(t, SingleQuery{
90-
NewMatch("key10", "XX"),
91-
NewMatch("key1", "a"),
92-
NewMatch("key2", "b"),
90+
NewEqual("key10", "XX"),
91+
NewEqual("key1", "a"),
92+
NewEqual("key2", "b"),
9393
}, res[0])
9494
assert.Equal(t, SingleQuery{
95-
NewMatch("key11", "YY"),
96-
NewMatch("key1", "a"),
97-
NewMatch("key2", "b"),
95+
NewEqual("key11", "YY"),
96+
NewEqual("key1", "a"),
97+
NewEqual("key2", "b"),
9898
}, res[1])
9999
assert.Equal(t, SingleQuery{
100-
NewMatch("key10", "XX"),
101-
NewMatch("key1", "AA"),
102-
NewMatch("key3", "CC"),
100+
NewEqual("key10", "XX"),
101+
NewEqual("key1", "AA"),
102+
NewEqual("key3", "CC"),
103103
}, res[2])
104104
assert.Equal(t, SingleQuery{
105-
NewMatch("key11", "YY"),
106-
NewMatch("key1", "AA"),
107-
NewMatch("key3", "CC"),
105+
NewEqual("key11", "YY"),
106+
NewEqual("key1", "AA"),
107+
NewEqual("key3", "CC"),
108108
}, res[3])
109109
assert.Equal(t, SingleQuery{
110-
NewMatch("key-ignore", "ZZ"),
110+
NewEqual("key-ignore", "ZZ"),
111111
}, res[4])
112112
}

pkg/model/filters/logql.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,26 +123,33 @@ func NotIPLabelFilter(labelKey, cidr string) LabelFilter {
123123
}
124124
}
125125

126-
func MultiValuesRegexFilter(labelKey string, values []string, not bool) (LabelFilter, bool) {
126+
func MultiValuesRegexFilter(labelKey string, values []string, not bool, equal bool) (LabelFilter, bool) {
127127
regexStr := strings.Builder{}
128128
for i, value := range values {
129129
if i > 0 {
130130
regexStr.WriteByte('|')
131131
}
132-
// match the begining of string if quoted without a star
133-
// and case insensitive if no quotes
134-
if !strings.HasPrefix(value, `"`) {
135-
regexStr.WriteString("(?i).*")
136-
} else if !strings.HasPrefix(value, `"*`) {
137-
regexStr.WriteString("^")
132+
133+
if !equal {
134+
// match the begining of string if quoted without a star
135+
// and case insensitive if no quotes
136+
if !strings.HasPrefix(value, `"`) {
137+
regexStr.WriteString("(?i).*")
138+
} else if !strings.HasPrefix(value, `"*`) {
139+
regexStr.WriteString("^")
140+
}
138141
}
142+
139143
// inject value with regex
140144
regexStr.WriteString(valueReplacer.Replace(value))
141-
// match the end of string if quoted without a star
142-
if !strings.HasSuffix(value, `"`) {
143-
regexStr.WriteString(".*")
144-
} else if !strings.HasSuffix(value, `*"`) {
145-
regexStr.WriteString("$")
145+
146+
if !equal {
147+
// match the end of string if quoted without a star
148+
if !strings.HasSuffix(value, `"`) {
149+
regexStr.WriteString(".*")
150+
} else if !strings.HasSuffix(value, `*"`) {
151+
regexStr.WriteString("$")
152+
}
146153
}
147154
}
148155

@@ -272,6 +279,11 @@ func ArrayLineFilter(key string, values []string, not bool) LineFilter {
272279
return lf
273280
}
274281

282+
// StringLineFilter returns a LineFilter and true if it has an empty match
283+
func StringLineFilter(key string, values []string, not bool) (LineFilter, bool) {
284+
return checkExact(LineFilter{key: key, not: not}, values, typeString)
285+
}
286+
275287
// StringLineFilterCheckExact returns a LineFilter and true if it has an empty match
276288
func StringLineFilterCheckExact(key string, values []string, not bool) (LineFilter, bool) {
277289
return checkExact(LineFilter{key: key, not: not}, values, typeRegexContains)

0 commit comments

Comments
 (0)