Skip to content

Commit 38bf991

Browse files
authored
Merge pull request #311 from Tetrergeru/feat/use-has-instead-of-arrayExists-if-possible
feature: Use `has` instead of `arrayExists` where possible
2 parents 80e2a49 + ede4f93 commit 38bf991

File tree

5 files changed

+22
-13
lines changed

5 files changed

+22
-13
lines changed

autocomplete/autocomplete.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,9 @@ func (h *Handler) ServeValues(w http.ResponseWriter, r *http.Request) {
529529
valueSQL = fmt.Sprintf("substr(Tag1, %d) AS value", len(tag)+2)
530530
wr.And(where.HasPrefix("Tag1", tag+"="+valuePrefix))
531531
} else {
532-
valueSQL = fmt.Sprintf("substr(arrayJoin(Tags), %d) AS value", len(tag)+2)
533-
wr.And(where.HasPrefix("arrayJoin(Tags)", tag+"="+valuePrefix))
532+
prefixSelector := where.HasPrefix("x", tag+"="+valuePrefix)
533+
valueSQL = fmt.Sprintf("substr(arrayFilter(x -> %s, Tags)[1], %d) AS value", prefixSelector, len(tag)+2)
534+
wr.And("arrayExists(x -> " + prefixSelector + ", Tags)")
534535
}
535536

536537
wr.Andf("Date >= '%s' AND Date <= '%s'", fromDate, untilDate)

autocomplete/autocomplete_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestHandler_ServeValues(t *testing.T) {
6868
fromDate, untilDate := dateString(h.config.ClickHouse.TaggedAutocompleDays, now)
6969

7070
srv.AddResponce(
71-
"SELECT substr(arrayJoin(Tags), 6) AS value FROM graphite_tagged WHERE (((Tag1='environment=production') AND (arrayExists((x) -> x='project=web', Tags))) AND (arrayJoin(Tags) LIKE 'host=%')) AND "+
71+
"SELECT substr(arrayFilter(x -> x LIKE 'host=%', Tags)[1], 6) AS value FROM graphite_tagged WHERE (((Tag1='environment=production') AND (has(Tags, 'project=web'))) AND (arrayExists(x -> x LIKE 'host=%', Tags))) AND "+
7272
"(Date >= '"+fromDate+"' AND Date <= '"+untilDate+"') GROUP BY value ORDER BY value LIMIT 10000",
7373
&chtest.TestResponse{
7474
Body: []byte("host1\nhost2\ndc-host2\ndc-host3\n"),
@@ -129,7 +129,7 @@ func TestTagsAutocomplete_ServeValuesCached(t *testing.T) {
129129
fromDate, untilDate := dateString(h.config.ClickHouse.TaggedAutocompleDays, now)
130130

131131
srv.AddResponce(
132-
"SELECT substr(arrayJoin(Tags), 6) AS value FROM graphite_tagged WHERE (((Tag1='environment=production') AND (arrayExists((x) -> x='project=web', Tags))) AND (arrayJoin(Tags) LIKE 'host=%')) AND "+
132+
"SELECT substr(arrayFilter(x -> x LIKE 'host=%', Tags)[1], 6) AS value FROM graphite_tagged WHERE (((Tag1='environment=production') AND (has(Tags, 'project=web'))) AND (arrayExists(x -> x LIKE 'host=%', Tags))) AND "+
133133
"(Date >= '"+fromDate+"' AND Date <= '"+untilDate+"') GROUP BY value ORDER BY value LIMIT 10000",
134134
&chtest.TestResponse{
135135
Body: []byte("host1\nhost2\ndc-host2\ndc-host3\n"),

finder/tagged.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,15 @@ func TaggedTermWhereN(term *TaggedTerm, useCarbonBehaviour, dontMatchMissingTags
188188
return "", err
189189
}
190190
if len(values) == 1 {
191-
return "arrayExists((x) -> " + where.Eq("x", values[0]) + ", Tags)", nil
191+
return where.ArrayHas("Tags", values[0]), nil
192192
} else if len(values) > 1 {
193-
return "arrayExists((x) -> " + where.In("x", values) + ", Tags)", nil
193+
w := where.New()
194+
for _, v := range values {
195+
w.Or(where.ArrayHas("Tags", v))
196+
}
197+
return w.String(), nil
194198
} else {
195-
return "arrayExists((x) -> " + where.Eq("x", term.concat()) + ", Tags)", nil
199+
return where.ArrayHas("Tags", term.concat()), nil
196200
}
197201
case TaggedTermNe:
198202
if term.Value == "" {

finder/tagged_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,15 @@ func TestTaggedWhere(t *testing.T) {
6565
{"seriesByTag('name=m{in}')", 0, "Tag1='__name__=min'", "", false},
6666
{"seriesByTag('name=m{in,ax}')", 0, "Tag1 IN ('__name__=min','__name__=max')", "", false},
6767
{"seriesByTag('name=m{in,ax')", 0, "", "", true},
68-
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
68+
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND ((has(Tags, 'what=avg')) OR (has(Tags, 'what=max')))", "", false},
6969
{"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
7070
// grafana workaround for multi-value variables default, masked with *
7171
{"seriesByTag('name=value','what=~*')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags))", "", false}, // If All masked to value with *
7272
// empty tag value during autocompletion
7373
{"seriesByTag('name=value','what=~')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags))", "", false}, // If All masked to value with *
7474
// testcases for useCarbonBehaviour=false
7575
{"seriesByTag('what=')", 0, "Tag1='what='", "", false},
76-
{"seriesByTag('name=value','what=')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x='what=', Tags))", "", false},
76+
{"seriesByTag('name=value','what=')", 0, "(Tag1='__name__=value') AND (has(Tags, 'what='))", "", false},
7777
// testcases for dontMatchMissingTags=false
7878
{"seriesByTag('key!=value')", 0, "NOT arrayExists((x) -> x='key=value', Tags)", "", false},
7979
{"seriesByTag('dc!=de', 'cpu=cpu-total')", 0, "(Tag1='cpu=cpu-total') AND (NOT arrayExists((x) -> x='dc=de', Tags))", "", false},
@@ -164,7 +164,7 @@ func TestTaggedWhere_UseCarbonBehaviourFlag(t *testing.T) {
164164
{"seriesByTag('name=m{in}')", 0, "Tag1='__name__=min'", "", false},
165165
{"seriesByTag('name=m{in,ax}')", 0, "Tag1 IN ('__name__=min','__name__=max')", "", false},
166166
{"seriesByTag('name=m{in,ax')", 0, "", "", true},
167-
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
167+
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND ((has(Tags, 'what=avg')) OR (has(Tags, 'what=max')))", "", false},
168168
{"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
169169
// grafana workaround for multi-value variables default, masked with *
170170
{"seriesByTag('name=value','what=~*')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags))", "", false}, // If All masked to value with *
@@ -264,7 +264,7 @@ func TestTaggedWhere_DontMatchMissingTagsFlag(t *testing.T) {
264264
{"seriesByTag('name=m{in}')", 0, "Tag1='__name__=min'", "", false},
265265
{"seriesByTag('name=m{in,ax}')", 0, "Tag1 IN ('__name__=min','__name__=max')", "", false},
266266
{"seriesByTag('name=m{in,ax')", 0, "", "", true},
267-
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
267+
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND ((has(Tags, 'what=avg')) OR (has(Tags, 'what=max')))", "", false},
268268
// {"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
269269
{"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags) AND NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
270270
// grafana workaround for multi-value variables default, masked with *
@@ -273,7 +273,7 @@ func TestTaggedWhere_DontMatchMissingTagsFlag(t *testing.T) {
273273
{"seriesByTag('name=value','what=~')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags))", "", false}, // If All masked to value with *
274274
// testcases for useCarbonBehaviour=false
275275
{"seriesByTag('what=')", 0, "Tag1='what='", "", false},
276-
{"seriesByTag('name=value','what=')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x='what=', Tags))", "", false},
276+
{"seriesByTag('name=value','what=')", 0, "(Tag1='__name__=value') AND (has(Tags, 'what='))", "", false},
277277
// testcases for dontMatchMissingTags=true
278278
{"seriesByTag('key!=value')", 0, "Tag1 LIKE 'key=%' AND NOT arrayExists((x) -> x='key=value', Tags)", "", false},
279279
{"seriesByTag('dc!=de', 'cpu=cpu-total')", 0, "(Tag1='cpu=cpu-total') AND (arrayExists((x) -> x LIKE 'dc=%', Tags) AND NOT arrayExists((x) -> x='dc=de', Tags))", "", false},
@@ -365,7 +365,7 @@ func TestTaggedWhere_BothFeatureFlags(t *testing.T) {
365365
{"seriesByTag('name=m{in}')", 0, "Tag1='__name__=min'", "", false},
366366
{"seriesByTag('name=m{in,ax}')", 0, "Tag1 IN ('__name__=min','__name__=max')", "", false},
367367
{"seriesByTag('name=m{in,ax')", 0, "", "", true},
368-
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
368+
{"seriesByTag('name=value','what={avg,max}')", 0, "(Tag1='__name__=value') AND ((has(Tags, 'what=avg')) OR (has(Tags, 'what=max')))", "", false},
369369
// {"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
370370
{"seriesByTag('name=value','what!={avg,max}')", 0, "(Tag1='__name__=value') AND (arrayExists((x) -> x LIKE 'what=%', Tags) AND NOT arrayExists((x) -> x IN ('what=avg','what=max'), Tags))", "", false},
371371
// grafana workaround for multi-value variables default, masked with *

pkg/where/where.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ func HasPrefixBytes(field, prefix []byte) string {
171171
return fmt.Sprintf("%s LIKE '%s%%'", field, likeEscape(unsafeString(prefix)))
172172
}
173173

174+
func ArrayHas(field, element string) string {
175+
return fmt.Sprintf("has(%s, %s)", field, quote(element))
176+
}
177+
174178
func In(field string, list []string) string {
175179
if len(list) == 1 {
176180
return Eq(field, list[0])

0 commit comments

Comments
 (0)