@@ -252,6 +252,7 @@ func NotContainsKeyLineFilter(key string) LineFilter {
252252 }
253253}
254254
255+ // NumericLineFilter returns a LineFilter and true if it has an empty match
255256func NumericLineFilter (key string , values []string , not , moreThan bool ) (LineFilter , bool ) {
256257 return checkExact (
257258 LineFilter {
@@ -271,6 +272,7 @@ func ArrayLineFilter(key string, values []string, not bool) LineFilter {
271272 return lf
272273}
273274
275+ // StringLineFilterCheckExact returns a LineFilter and true if it has an empty match
274276func StringLineFilterCheckExact (key string , values []string , not bool ) (LineFilter , bool ) {
275277 return checkExact (LineFilter {key : key , not : not }, values , typeRegexContains )
276278}
@@ -364,21 +366,11 @@ func moreThanRegex(sb *strings.Builder, value string) {
364366// under construction (contained in the provided strings.Builder)
365367func (f * LineFilter ) WriteInto (sb * strings.Builder ) {
366368 if f .not {
367- if ! f .allowEmpty {
368- // the record must contains the field if values are specified
369- // since FLP skip empty fields / zeros values
370- if len (f .values ) > 0 {
371- sb .WriteString ("|~`\" " )
372- sb .WriteString (f .key )
373- sb .WriteString ("\" `" )
374- }
375- }
376- // then we exclude match results
377- sb .WriteString ("!~`" )
378- } else {
379- sb .WriteString ("|~`" )
369+ f .writeIntoNot (sb )
370+ return
380371 }
381372
373+ sb .WriteString ("|~`" )
382374 if len (f .values ) == 0 {
383375 // match only the end of KEY if not 'strictKey'
384376 // no value will be provided here as we only check if key exists
@@ -392,49 +384,85 @@ func (f *LineFilter) WriteInto(sb *strings.Builder) {
392384 if i > 0 {
393385 sb .WriteByte ('|' )
394386 }
387+ f .writeValueInto (sb , v )
388+ }
389+ }
390+ sb .WriteRune ('`' )
391+ }
395392
396- // match only the end of KEY + regex VALUE if not 'strictKey'
397- // if numeric, KEY":VALUE,
398- // if string KEY":"VALUE"
399- // ie 'Port' key will match both 'SrcPort":"XXX"' and 'DstPort":"XXX"
400- // VALUE can be quoted for exact match or contains * to inject regex any
401- // For numeric values, exact match is implicit
402- // (the trick is to match for the ending coma; it works as long as the filtered field
403- // is not the last one (they're in alphabetic order); a less performant alternative
404- // but more future-proof/less hacky could be to move that to a json filter, if needed)
405- if f .strictKey {
406- sb .WriteByte ('"' )
407- }
393+ // WriteInto transforms a LineFilter to its corresponding part of a LogQL query
394+ // under construction (contained in the provided strings.Builder)
395+ func (f * LineFilter ) writeIntoNot (sb * strings.Builder ) {
396+ if ! f .allowEmpty {
397+ // the record must contains the field if values are specified
398+ // since FLP skip empty fields / zeros values
399+ if len (f .values ) > 0 {
400+ sb .WriteString ("|~`\" " )
408401 sb .WriteString (f .key )
409- sb .WriteString (`":` )
410- switch v .valueType {
411- case typeNumber , typeRegex :
412- if f .moreThan {
413- moreThanRegex (sb , v .value )
414- } else {
415- sb .WriteString (v .value )
416- }
417- // a number or regex can be followed by } if it's the last property of a JSON document
418- sb .WriteString ("[,}]" )
419- case typeBool :
420- sb .WriteString (v .value )
421- case typeString , typeIP :
422- // exact matches are specified as just strings
423- sb .WriteByte ('"' )
424- sb .WriteString (valueReplacer .Replace (v .value ))
425- sb .WriteByte ('"' )
426- // contains-match are specified as regular expressions
427- case typeRegexContains :
428- sb .WriteString (`"(?i)[^"]*` )
429- sb .WriteString (valueReplacer .Replace (v .value ))
430- sb .WriteString (`.*"` )
431- // for array, we ensure it starts by [ and ends by ]
432- case typeRegexArrayContains :
433- sb .WriteString (`\[(?i)[^]]*` )
434- sb .WriteString (valueReplacer .Replace (v .value ))
435- sb .WriteString (`[^]]*]` )
436- }
402+ sb .WriteString ("\" `" )
437403 }
438404 }
439- sb .WriteRune ('`' )
405+
406+ if len (f .values ) == 0 {
407+ // then we exclude match results
408+ sb .WriteString ("!~`" )
409+
410+ // match only the end of KEY if not 'strictKey'
411+ // no value will be provided here as we only check if key exists
412+ if f .strictKey {
413+ sb .WriteByte ('"' )
414+ }
415+ sb .WriteString (f .key )
416+ sb .WriteString ("\" `" )
417+ } else {
418+ for _ , v := range f .values {
419+ sb .WriteString ("!~`" )
420+ f .writeValueInto (sb , v )
421+ sb .WriteRune ('`' )
422+ }
423+ }
424+ }
425+
426+ func (f * LineFilter ) writeValueInto (sb * strings.Builder , v lineMatch ) {
427+ // match only the end of KEY + regex VALUE if not 'strictKey'
428+ // if numeric, KEY":VALUE,
429+ // if string KEY":"VALUE"
430+ // ie 'Port' key will match both 'SrcPort":"XXX"' and 'DstPort":"XXX"
431+ // VALUE can be quoted for exact match or contains * to inject regex any
432+ // For numeric values, exact match is implicit
433+ // (the trick is to match for the ending coma; it works as long as the filtered field
434+ // is not the last one (they're in alphabetic order); a less performant alternative
435+ // but more future-proof/less hacky could be to move that to a json filter, if needed)
436+ if f .strictKey {
437+ sb .WriteByte ('"' )
438+ }
439+ sb .WriteString (f .key )
440+ sb .WriteString (`":` )
441+ switch v .valueType {
442+ case typeNumber , typeRegex :
443+ if f .moreThan {
444+ moreThanRegex (sb , v .value )
445+ } else {
446+ sb .WriteString (v .value )
447+ }
448+ // a number or regex can be followed by } if it's the last property of a JSON document
449+ sb .WriteString ("[,}]" )
450+ case typeBool :
451+ sb .WriteString (v .value )
452+ case typeString , typeIP :
453+ // exact matches are specified as just strings
454+ sb .WriteByte ('"' )
455+ sb .WriteString (valueReplacer .Replace (v .value ))
456+ sb .WriteByte ('"' )
457+ // contains-match are specified as regular expressions
458+ case typeRegexContains :
459+ sb .WriteString (`"(?i)[^"]*` )
460+ sb .WriteString (valueReplacer .Replace (v .value ))
461+ sb .WriteString (`.*"` )
462+ // for array, we ensure it starts by [ and ends by ]
463+ case typeRegexArrayContains :
464+ sb .WriteString (`\[(?i)[^]]*` )
465+ sb .WriteString (valueReplacer .Replace (v .value ))
466+ sb .WriteString (`[^]]*]` )
467+ }
440468}
0 commit comments