Skip to content

Commit a400919

Browse files
committed
correct processing of {a=""} and {a!=""} cases
1 parent 8ddaf1e commit a400919

File tree

1 file changed

+78
-41
lines changed

1 file changed

+78
-41
lines changed

promql/index.js

+78-41
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ module.exports.series = async (query, fromMs, toMs) => {
4545
const fromS = Math.floor(fromMs / 1000)
4646
const toS = Math.floor(toMs / 1000)
4747
const matchers = prometheus.pqlMatchers(query)
48-
const conds = getMatchersIdxCond(matchers[0])
49-
const idx = getIdxSubquery(conds, fromMs, toMs)
48+
const idx = getIdxSubqueryV2(matchers[0], fromMs, toMs)
5049
const withIdx = new Sql.With('idx', idx, !!clusterName)
5150
const req = (new Sql.Select())
5251
.with(withIdx)
@@ -70,51 +69,90 @@ module.exports.series = async (query, fromMs, toMs) => {
7069
}
7170
}
7271

72+
/**
73+
*
74+
* @param matcher {[string]}
75+
*/
76+
const getMatcherIdxCond = (matcher) => {
77+
const res = [
78+
Sql.Eq('key', matcher[0])
79+
]
80+
switch (matcher[1]) {
81+
case '=':
82+
res.push(Sql.Eq('val', matcher[2]))
83+
break
84+
case '!=':
85+
res.push(Sql.Ne('val', matcher[2]))
86+
break
87+
case '=~':
88+
res.push(Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1))
89+
break
90+
case '!~':
91+
res.push(Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1))
92+
}
93+
return res
94+
}
95+
7396
/**
7497
*
7598
* @param matchers {[[string]]}
7699
*/
77100
const getMatchersIdxCond = (matchers) => {
78-
const matchesCond = []
79-
for (const matcher of matchers) {
80-
const _matcher = [
81-
Sql.Eq('key', matcher[0])
82-
]
83-
switch (matcher[1]) {
84-
case '=':
85-
_matcher.push(Sql.Eq('val', matcher[2]))
86-
break
87-
case '!=':
88-
_matcher.push(Sql.Ne('val', matcher[2]))
89-
break
90-
case '=~':
91-
_matcher.push(Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1))
92-
break
93-
case '!~':
94-
_matcher.push(Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1))
95-
}
96-
matchesCond.push(Sql.And(..._matcher))
97-
}
98-
return matchesCond
101+
return matchers.map(matcher => Sql.And(...getMatcherIdxCond(matcher)))
99102
}
100103

101-
const getIdxSubquery = (conds, fromMs, toMs) => {
104+
const getIdxSubqueryV2 = (matchers, fromMs, toMs) => {
102105
const fromS = Math.floor(fromMs / 1000)
103106
const toS = Math.floor(toMs / 1000)
104-
return (new Sql.Select())
105-
.select('fingerprint')
106-
.from([DATABASE_NAME() + '.time_series_gin', 'time_series_gin'])
107-
.where(Sql.And(
108-
Sql.Or(...conds),
109-
Sql.Gte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${fromS}))`)),
110-
Sql.Lte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${toS}))`)),
111-
new Sql.In('type', 'in', [bothType, metricType])))
112-
.having(
113-
Sql.Eq(
114-
new Sql.Raw('groupBitOr(' + conds.map(
115-
(m, i) => new Sql.Raw(`bitShiftLeft((${m})::UInt64, ${i})`)
116-
).join('+') + ')'), (1 << conds.length) - 1)
117-
).groupBy('fingerprint')
107+
const nonEmptyMatchers = matchers.filter(m => m[2] !== '')
108+
const emptyMatchers = matchers.filter(m => m[2] === '' && ['=', '!='].includes(m[1]))
109+
let req = null
110+
if (nonEmptyMatchers.length) {
111+
const nonEmptyConds = getMatchersIdxCond(nonEmptyMatchers)
112+
req = (new Sql.Select())
113+
.select('fingerprint')
114+
.from([DATABASE_NAME() + '.time_series_gin', 'time_series_gin'])
115+
.where(Sql.And(
116+
Sql.Or(...nonEmptyConds),
117+
Sql.Gte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${fromS}))`)),
118+
Sql.Lte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${toS}))`)),
119+
new Sql.In('type', 'in', [bothType, metricType])))
120+
.having(
121+
Sql.Eq(
122+
new Sql.Raw('groupBitOr(' + nonEmptyConds.map(
123+
(m, i) => new Sql.Raw(`bitShiftLeft((${m})::UInt64, ${i})`)
124+
).join('+') + ')'), (1 << nonEmptyConds.length) - 1)
125+
).groupBy('fingerprint')
126+
}
127+
if (emptyMatchers.length) {
128+
const emptyConds = emptyMatchers.map(m => {
129+
const visitParamHas = new Sql.Raw('')
130+
visitParamHas.toString = function () {
131+
return `visitParamHas(labels, ${Sql.quoteVal(m[0])})`
132+
}
133+
switch (m[1]) {
134+
case '=':
135+
return Sql.Eq(visitParamHas, new Sql.Raw('0'))
136+
case '!=':
137+
return Sql.Ne(visitParamHas, new Sql.Raw('1'))
138+
default:
139+
return null
140+
}
141+
}).filter(m => !!m)
142+
const emptyReq = (new Sql.Select())
143+
.select('fingerprint')
144+
.from(`time_series${_dist}`)
145+
.where(Sql.And(...emptyConds))
146+
if (nonEmptyMatchers.length) {
147+
const withNonEmptyIdx = new Sql.With('nonEmptyIdx', req, !!clusterName)
148+
emptyReq.with(withNonEmptyIdx)
149+
.where(
150+
new Sql.In('fingerprint', 'in', new Sql.WithReference(withNonEmptyIdx))
151+
)
152+
}
153+
req = emptyReq
154+
}
155+
return req
118156
}
119157

120158
module.exports.getData = async (matchers, fromMs, toMs, subqueries) => {
@@ -126,8 +164,7 @@ module.exports.getData = async (matchers, fromMs, toMs, subqueries) => {
126164
null, db, { responseType: 'arraybuffer' })
127165
return new Uint8Array(data.data)
128166
}
129-
const matches = getMatchersIdxCond(matchers)
130-
const idx = getIdxSubquery(matches, fromMs, toMs)
167+
const idx = getIdxSubqueryV2(matchers, fromMs, toMs)
131168
const withIdx = new Sql.With('idx', idx, !!clusterName)
132169
const timeSeries = (new Sql.Select())
133170
.select(
@@ -136,7 +173,7 @@ module.exports.getData = async (matchers, fromMs, toMs, subqueries) => {
136173
).from(DATABASE_NAME() + '.time_series')
137174
.where(Sql.And(
138175
new Sql.In('fingerprint', 'in', new Sql.WithReference(withIdx)),
139-
new Sql.In('type', 'in', [bothType,metricType])))
176+
new Sql.In('type', 'in', [bothType, metricType])))
140177
const withTimeSeries = new Sql.With('timeSeries', timeSeries, !!clusterName)
141178
const raw = (new Sql.Select())
142179
.with(withIdx)

0 commit comments

Comments
 (0)