@@ -108,20 +108,14 @@ func (s *PromQLSmith) walkBinaryExpr(depth int, valueTypes ...parser.ValueType)
108
108
valueTypes = []parser.ValueType {parser .ValueTypeVector }
109
109
expr .VectorMatching .Card = parser .CardManyToMany
110
110
}
111
- expr .LHS = wrapParenExpr (s .walk (depth - 1 , valueTypes ... ))
112
- expr .RHS = wrapParenExpr (s .walk (depth - 1 , valueTypes ... ))
113
- lvt := expr .LHS .Type ()
114
- rvt := expr .RHS .Type ()
115
- // ReturnBool can only be set for comparison operator. It is
116
- // required to set to true if both expressions are scalar type.
117
- if expr .Op .IsComparisonOperator () {
118
- if lvt == parser .ValueTypeScalar && rvt == parser .ValueTypeScalar || s .rnd .Intn (2 ) == 0 {
119
- expr .ReturnBool = true
120
- }
121
- }
122
111
123
- if ! expr .Op .IsSetOperator () && s .enableVectorMatching && lvt == parser .ValueTypeVector &&
124
- rvt == parser .ValueTypeVector && s .rnd .Intn (2 ) == 0 {
112
+ // Generate vector matching only if we know it asks for vector value type.
113
+ if ! expr .Op .IsSetOperator () && len (valueTypes ) == 1 && valueTypes [0 ] == parser .ValueTypeVector && s .enableVectorMatching && s .rnd .Float64 () > 0.8 {
114
+ lhs , _ := s .walkExpr (VectorSelector , depth - 1 , valueTypes ... )
115
+ expr .LHS = wrapParenExpr (lhs )
116
+ rhs , _ := s .walkExpr (VectorSelector , depth - 1 , valueTypes ... )
117
+ expr .RHS = wrapParenExpr (rhs )
118
+
125
119
leftSeriesSet , stop := getOutputSeries (expr .LHS )
126
120
if stop {
127
121
return expr
@@ -130,12 +124,25 @@ func (s *PromQLSmith) walkBinaryExpr(depth int, valueTypes ...parser.ValueType)
130
124
if stop {
131
125
return expr
132
126
}
133
- s .walkVectorMatching (expr , leftSeriesSet , rightSeriesSet , s .rnd .Intn (4 ) == 0 )
127
+ s .walkVectorMatching (expr , leftSeriesSet , rightSeriesSet , s .rnd .Intn (2 ) == 0 , s .rnd .Intn (4 ) == 0 )
128
+ } else {
129
+ expr .LHS = wrapParenExpr (s .walk (depth - 1 , valueTypes ... ))
130
+ expr .RHS = wrapParenExpr (s .walk (depth - 1 , valueTypes ... ))
131
+ }
132
+
133
+ lvt := expr .LHS .Type ()
134
+ rvt := expr .RHS .Type ()
135
+ // ReturnBool can only be set for comparison operator. It is
136
+ // required to set to true if both expressions are scalar type.
137
+ if expr .Op .IsComparisonOperator () {
138
+ if lvt == parser .ValueTypeScalar && rvt == parser .ValueTypeScalar || s .rnd .Intn (2 ) == 0 {
139
+ expr .ReturnBool = true
140
+ }
134
141
}
135
142
return expr
136
143
}
137
144
138
- func (s * PromQLSmith ) walkVectorMatching (expr * parser.BinaryExpr , seriesSetA []labels.Labels , seriesSetB []labels.Labels , includeLabels bool ) {
145
+ func (s * PromQLSmith ) walkVectorMatching (expr * parser.BinaryExpr , seriesSetA []labels.Labels , seriesSetB []labels.Labels , on , includeLabels bool ) {
139
146
sa := make (map [string ]struct {})
140
147
for _ , series := range seriesSetA {
141
148
series .Range (func (lbl labels.Label ) {
@@ -155,48 +162,129 @@ func (s *PromQLSmith) walkVectorMatching(expr *parser.BinaryExpr, seriesSetA []l
155
162
sb [lbl .Name ] = struct {}{}
156
163
})
157
164
}
158
- expr .VectorMatching .On = true
159
- matchedLabels := make ([]string , 0 )
165
+
166
+ // Find all matching labels
167
+ allMatchedLabels := make ([]string , 0 )
160
168
for key := range sb {
161
169
if _ , ok := sa [key ]; ok {
162
- matchedLabels = append (matchedLabels , key )
170
+ allMatchedLabels = append (allMatchedLabels , key )
163
171
}
164
172
}
173
+ // If there is no matching labels, we don't need to do vector matching.
174
+ if len (allMatchedLabels ) == 0 {
175
+ return
176
+ }
177
+
178
+ // Randomly select a subset of matched labels
179
+ sort .Strings (allMatchedLabels ) // Sort for deterministic selection
180
+ numLabels := s .rnd .Intn (len (allMatchedLabels )) + 1 // Select at least 1 label
181
+ selectedIndices := s .rnd .Perm (len (allMatchedLabels ))[:numLabels ]
182
+ sort .Ints (selectedIndices ) // Sort indices for consistent order
183
+
184
+ matchedLabels := make ([]string , numLabels )
185
+ for i , idx := range selectedIndices {
186
+ matchedLabels [i ] = allMatchedLabels [idx ]
187
+ }
188
+
189
+ expr .VectorMatching .On = on
190
+
165
191
// We are doing a very naive approach of guessing side cardinalities
166
192
// by checking number of series each side.
167
193
oneSideLabelsSet := sa
168
- if len ( seriesSetA ) > len ( seriesSetB ) {
194
+ if expr . VectorMatching . On {
169
195
expr .VectorMatching .MatchingLabels = matchedLabels
196
+ } else {
197
+ // For 'ignoring', we need to use all labels except the matched ones
198
+ expr .VectorMatching .MatchingLabels = getDifference (getAllLabels (sa ), matchedLabels )
199
+ }
200
+
201
+ if len (seriesSetA ) > len (seriesSetB ) {
170
202
expr .VectorMatching .Card = parser .CardManyToOne
171
203
oneSideLabelsSet = sb
172
204
} else if len (seriesSetA ) < len (seriesSetB ) {
173
- expr .VectorMatching .MatchingLabels = matchedLabels
174
205
expr .VectorMatching .Card = parser .CardOneToMany
175
206
}
207
+
176
208
// Otherwise we do 1:1 match.
177
209
178
- // For simplicity, we always include all labels on the one side.
179
210
if expr .VectorMatching .Card != parser .CardOneToOne && includeLabels {
180
- includeLabels := getIncludeLabels ( oneSideLabelsSet , matchedLabels )
211
+ includeLabels := getRandomIncludeLabels ( s . rnd , oneSideLabelsSet , expr . VectorMatching . MatchingLabels )
181
212
expr .VectorMatching .Include = includeLabels
182
213
}
183
214
}
184
215
216
+ // Helper function to get all labels from a map
217
+ func getAllLabels (labelSet map [string ]struct {}) []string {
218
+ labels := make ([]string , 0 , len (labelSet ))
219
+ for label := range labelSet {
220
+ labels = append (labels , label )
221
+ }
222
+ sort .Strings (labels )
223
+ return labels
224
+ }
225
+
226
+ // Helper function to get the difference between two sorted string slices
227
+ func getDifference (all , exclude []string ) []string {
228
+ result := make ([]string , 0 )
229
+ excludeMap := make (map [string ]struct {})
230
+ for _ , e := range exclude {
231
+ excludeMap [e ] = struct {}{}
232
+ }
233
+
234
+ for _ , label := range all {
235
+ if _ , exists := excludeMap [label ]; ! exists {
236
+ result = append (result , label )
237
+ }
238
+ }
239
+ return result
240
+ }
241
+
242
+ // Helper function to get all eligible labels that aren't in the matched set
185
243
func getIncludeLabels (labelNameSet map [string ]struct {}, matchedLabels []string ) []string {
244
+ // Create a map of matched labels for quick lookup
245
+ matchedSet := make (map [string ]struct {})
246
+ for _ , label := range matchedLabels {
247
+ matchedSet [label ] = struct {}{}
248
+ }
249
+
250
+ // Collect all eligible labels that aren't in the matched set
186
251
output := make ([]string , 0 )
187
- OUTER:
188
252
for lbl := range labelNameSet {
189
- for _ , matchedLabel := range matchedLabels {
190
- if lbl == matchedLabel {
191
- continue OUTER
192
- }
253
+ if _ , matched := matchedSet [lbl ]; ! matched {
254
+ output = append (output , lbl )
193
255
}
194
- output = append (output , lbl )
195
256
}
257
+
258
+ // Sort for deterministic output
196
259
sort .Strings (output )
197
260
return output
198
261
}
199
262
263
+ // Helper function to randomly select a subset of include labels
264
+ func getRandomIncludeLabels (rnd * rand.Rand , labelNameSet map [string ]struct {}, matchedLabels []string ) []string {
265
+ eligible := getIncludeLabels (labelNameSet , matchedLabels )
266
+ if len (eligible ) == 0 {
267
+ return nil
268
+ }
269
+
270
+ // Pick a random number of labels to include (at least 1 if available)
271
+ numLabels := rnd .Intn (len (eligible )) + 1
272
+ if numLabels > len (eligible ) {
273
+ numLabels = len (eligible )
274
+ }
275
+
276
+ // Randomly select the labels
277
+ indices := rnd .Perm (len (eligible ))[:numLabels ]
278
+ sort .Ints (indices )
279
+
280
+ // Create the final selection
281
+ result := make ([]string , numLabels )
282
+ for i , idx := range indices {
283
+ result [i ] = eligible [idx ]
284
+ }
285
+ return result
286
+ }
287
+
200
288
// Walk binary op based on whether vector value type is allowed or not.
201
289
// Since Set operator only works with vector so if vector is disallowed
202
290
// we will choose comparison operator that works both for scalar and vector.
0 commit comments