mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-31 15:06:26 +00:00
wip
This commit is contained in:
parent
e3cbf97bdd
commit
a686c7dd74
3 changed files with 56 additions and 44 deletions
|
@ -5,8 +5,6 @@ import (
|
||||||
"html"
|
"html"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// pattern represents text pattern in the form 'some_text<some_field>other_text...'
|
// pattern represents text pattern in the form 'some_text<some_field>other_text...'
|
||||||
|
@ -28,18 +26,25 @@ type patternField struct {
|
||||||
|
|
||||||
type patternStep struct {
|
type patternStep struct {
|
||||||
prefix string
|
prefix string
|
||||||
field string
|
|
||||||
opt string
|
field string
|
||||||
|
fieldOpt string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ptn *pattern) clone() *pattern {
|
func (ptn *pattern) clone() *pattern {
|
||||||
steps := ptn.steps
|
matches := make([]string, len(ptn.steps))
|
||||||
fields, matches := newFieldsAndMatchesFromPatternSteps(steps)
|
var fields []patternField
|
||||||
if len(fields) == 0 {
|
for i, step := range ptn.steps {
|
||||||
logger.Panicf("BUG: fields cannot be empty for steps=%v", steps)
|
if step.field != "" {
|
||||||
|
fields = append(fields, patternField{
|
||||||
|
name: step.field,
|
||||||
|
value: &matches[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pattern{
|
return &pattern{
|
||||||
steps: steps,
|
steps: ptn.steps,
|
||||||
matches: matches,
|
matches: matches,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
}
|
}
|
||||||
|
@ -59,7 +64,18 @@ func parsePattern(s string) (*pattern, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build pattern struct
|
// Build pattern struct
|
||||||
fields, matches := newFieldsAndMatchesFromPatternSteps(steps)
|
|
||||||
|
matches := make([]string, len(steps))
|
||||||
|
|
||||||
|
var fields []patternField
|
||||||
|
for i, step := range steps {
|
||||||
|
if step.field != "" {
|
||||||
|
fields = append(fields, patternField{
|
||||||
|
name: step.field,
|
||||||
|
value: &matches[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
return nil, fmt.Errorf("pattern %q must contain at least a single named field in the form <field_name>", s)
|
return nil, fmt.Errorf("pattern %q must contain at least a single named field in the form <field_name>", s)
|
||||||
}
|
}
|
||||||
|
@ -72,35 +88,17 @@ func parsePattern(s string) (*pattern, error) {
|
||||||
return ptn, nil
|
return ptn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFieldsAndMatchesFromPatternSteps(steps []patternStep) ([]patternField, []string) {
|
|
||||||
matches := make([]string, len(steps))
|
|
||||||
|
|
||||||
var fields []patternField
|
|
||||||
for i, step := range steps {
|
|
||||||
if step.field != "" {
|
|
||||||
fields = append(fields, patternField{
|
|
||||||
name: step.field,
|
|
||||||
value: &matches[i],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields, matches
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ptn *pattern) apply(s string) {
|
func (ptn *pattern) apply(s string) {
|
||||||
clear(ptn.matches)
|
clear(ptn.matches)
|
||||||
|
|
||||||
steps := ptn.steps
|
steps := ptn.steps
|
||||||
|
|
||||||
if prefix := steps[0].prefix; prefix != "" {
|
n, prefixLen := prefixIndex(s, steps[0].prefix)
|
||||||
n := strings.Index(s, prefix)
|
if n < 0 {
|
||||||
if n < 0 {
|
// Mismatch
|
||||||
// Mismatch
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
s = s[n+len(prefix):]
|
|
||||||
}
|
}
|
||||||
|
s = s[n+prefixLen:]
|
||||||
|
|
||||||
matches := ptn.matches
|
matches := ptn.matches
|
||||||
for i := range steps {
|
for i := range steps {
|
||||||
|
@ -109,7 +107,7 @@ func (ptn *pattern) apply(s string) {
|
||||||
nextPrefix = steps[i+1].prefix
|
nextPrefix = steps[i+1].prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
us, nOffset := tryUnquoteString(s, steps[i].opt)
|
us, nOffset := tryUnquoteString(s, steps[i].fieldOpt)
|
||||||
if nOffset >= 0 {
|
if nOffset >= 0 {
|
||||||
// Matched quoted string
|
// Matched quoted string
|
||||||
matches[i] = us
|
matches[i] = us
|
||||||
|
@ -125,17 +123,28 @@ func (ptn *pattern) apply(s string) {
|
||||||
matches[i] = s
|
matches[i] = s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n := strings.Index(s, nextPrefix)
|
n, prefixLen := prefixIndex(s, nextPrefix)
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
// Mismatch
|
// Mismatch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
matches[i] = s[:n]
|
matches[i] = s[:n]
|
||||||
s = s[n+len(nextPrefix):]
|
s = s[n+prefixLen:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prefixIndex(s, prefix string) (int, int) {
|
||||||
|
if len(prefix) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
n := strings.Index(s, prefix)
|
||||||
|
if n < 0 {
|
||||||
|
return -1, 0
|
||||||
|
}
|
||||||
|
return n, len(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
func tryUnquoteString(s, opt string) (string, int) {
|
func tryUnquoteString(s, opt string) (string, int) {
|
||||||
if opt == "plain" {
|
if opt == "plain" {
|
||||||
return "", -1
|
return "", -1
|
||||||
|
@ -163,7 +172,7 @@ func parsePatternSteps(s string) ([]patternStep, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unescape prefixes
|
// unescape prefixes
|
||||||
for i := range steps {
|
for i := range steps {
|
||||||
step := &steps[i]
|
step := &steps[i]
|
||||||
step.prefix = html.UnescapeString(step.prefix)
|
step.prefix = html.UnescapeString(step.prefix)
|
||||||
|
@ -174,7 +183,7 @@ func parsePatternSteps(s string) ([]patternStep, error) {
|
||||||
step := &steps[i]
|
step := &steps[i]
|
||||||
field := step.field
|
field := step.field
|
||||||
if n := strings.IndexByte(field, ':'); n >= 0 {
|
if n := strings.IndexByte(field, ':'); n >= 0 {
|
||||||
step.opt = strings.TrimSpace(field[:n])
|
step.fieldOpt = strings.TrimSpace(field[:n])
|
||||||
field = field[n+1:]
|
field = field[n+1:]
|
||||||
}
|
}
|
||||||
step.field = strings.TrimSpace(field)
|
step.field = strings.TrimSpace(field)
|
||||||
|
|
|
@ -63,6 +63,9 @@ func TestPatternApply(t *testing.T) {
|
||||||
f(`foo=<bar> `, "foo=`bar baz,abc` def", []string{"bar baz,abc"})
|
f(`foo=<bar> `, "foo=`bar baz,abc` def", []string{"bar baz,abc"})
|
||||||
f(`<foo>`, `"foo,\"bar"`, []string{`foo,"bar`})
|
f(`<foo>`, `"foo,\"bar"`, []string{`foo,"bar`})
|
||||||
f(`<foo>,"bar`, `"foo,\"bar"`, []string{`foo,"bar`})
|
f(`<foo>,"bar`, `"foo,\"bar"`, []string{`foo,"bar`})
|
||||||
|
|
||||||
|
// disable automatic unquoting of quoted field
|
||||||
|
f(`[<plain:foo>]`, `["foo","bar"]`, []string{`"foo","bar"`})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParsePatternFailure(t *testing.T) {
|
func TestParsePatternFailure(t *testing.T) {
|
||||||
|
@ -207,13 +210,13 @@ func TestParsePatternStepsSuccess(t *testing.T) {
|
||||||
})
|
})
|
||||||
f("< q : foo >bar<plain : baz:c:y>f<:foo:bar:baz>", []patternStep{
|
f("< q : foo >bar<plain : baz:c:y>f<:foo:bar:baz>", []patternStep{
|
||||||
{
|
{
|
||||||
field: "foo",
|
field: "foo",
|
||||||
opt: "q",
|
fieldOpt: "q",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefix: "bar",
|
prefix: "bar",
|
||||||
field: "baz:c:y",
|
field: "baz:c:y",
|
||||||
opt: "plain",
|
fieldOpt: "plain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefix: "f",
|
prefix: "f",
|
||||||
|
|
|
@ -137,7 +137,7 @@ func (shard *pipeFormatProcessorShard) formatRow(pf *pipeFormat, br *blockResult
|
||||||
if step.field != "" {
|
if step.field != "" {
|
||||||
c := br.getColumnByName(step.field)
|
c := br.getColumnByName(step.field)
|
||||||
v := c.getValueAtRow(br, rowIdx)
|
v := c.getValueAtRow(br, rowIdx)
|
||||||
if step.opt == "q" {
|
if step.fieldOpt == "q" {
|
||||||
b = strconv.AppendQuote(b, v)
|
b = strconv.AppendQuote(b, v)
|
||||||
} else {
|
} else {
|
||||||
b = append(b, v...)
|
b = append(b, v...)
|
||||||
|
|
Loading…
Reference in a new issue