mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
(Non-working) prototype for doing joins on unequal labels.
Allows you to do things like 'foo * on(bar==baz) blat'... Signed-off-by: Tom Wilkie <tom@grafana.com>
This commit is contained in:
parent
820d7775eb
commit
ac4594aaf4
|
@ -232,7 +232,7 @@ type VectorMatching struct {
|
|||
Card VectorMatchCardinality
|
||||
// MatchingLabels contains the labels which define equality of a pair of
|
||||
// elements from the Vectors.
|
||||
MatchingLabels []string
|
||||
MatchingLabels []*labels.Matcher
|
||||
// On includes the given label names from matching,
|
||||
// rather than excluding them.
|
||||
On bool
|
||||
|
@ -241,6 +241,22 @@ type VectorMatching struct {
|
|||
Include []string
|
||||
}
|
||||
|
||||
func justNames(in []*labels.Matcher) []string {
|
||||
var result = make([]string, 0, len(in))
|
||||
for _, i := range in {
|
||||
result = append(result, i.Name)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func justValues(in []*labels.Matcher) []string {
|
||||
var result = make([]string, 0, len(in))
|
||||
for _, i := range in {
|
||||
result = append(result, i.Value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Visitor allows visiting a Node and its child nodes. The Visit method is
|
||||
// invoked for each node with the path leading to the node provided additionally.
|
||||
// If the result visitor w is not nil and no error, Walk visits each of the children
|
||||
|
|
|
@ -1519,18 +1519,19 @@ func (ev *evaluator) VectorAnd(lhs, rhs Vector, matching *VectorMatching, enh *E
|
|||
if matching.Card != CardManyToMany {
|
||||
panic("set operations must only use many-to-many matching")
|
||||
}
|
||||
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
|
||||
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
|
||||
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
|
||||
|
||||
// The set of signatures for the right-hand side Vector.
|
||||
rightSigs := map[uint64]struct{}{}
|
||||
// Add all rhs samples to a map so we can easily find matches later.
|
||||
for _, rs := range rhs {
|
||||
rightSigs[sigf(rs.Metric)] = struct{}{}
|
||||
rightSigs[sigfr(rs.Metric)] = struct{}{}
|
||||
}
|
||||
|
||||
for _, ls := range lhs {
|
||||
// If there's a matching entry in the right-hand side Vector, add the sample.
|
||||
if _, ok := rightSigs[sigf(ls.Metric)]; ok {
|
||||
if _, ok := rightSigs[sigfl(ls.Metric)]; ok {
|
||||
enh.out = append(enh.out, ls)
|
||||
}
|
||||
}
|
||||
|
@ -1541,17 +1542,18 @@ func (ev *evaluator) VectorOr(lhs, rhs Vector, matching *VectorMatching, enh *Ev
|
|||
if matching.Card != CardManyToMany {
|
||||
panic("set operations must only use many-to-many matching")
|
||||
}
|
||||
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
|
||||
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
|
||||
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
|
||||
|
||||
leftSigs := map[uint64]struct{}{}
|
||||
// Add everything from the left-hand-side Vector.
|
||||
for _, ls := range lhs {
|
||||
leftSigs[sigf(ls.Metric)] = struct{}{}
|
||||
leftSigs[sigfl(ls.Metric)] = struct{}{}
|
||||
enh.out = append(enh.out, ls)
|
||||
}
|
||||
// Add all right-hand side elements which have not been added from the left-hand side.
|
||||
for _, rs := range rhs {
|
||||
if _, ok := leftSigs[sigf(rs.Metric)]; !ok {
|
||||
if _, ok := leftSigs[sigfr(rs.Metric)]; !ok {
|
||||
enh.out = append(enh.out, rs)
|
||||
}
|
||||
}
|
||||
|
@ -1562,15 +1564,16 @@ func (ev *evaluator) VectorUnless(lhs, rhs Vector, matching *VectorMatching, enh
|
|||
if matching.Card != CardManyToMany {
|
||||
panic("set operations must only use many-to-many matching")
|
||||
}
|
||||
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
|
||||
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
|
||||
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
|
||||
|
||||
rightSigs := map[uint64]struct{}{}
|
||||
for _, rs := range rhs {
|
||||
rightSigs[sigf(rs.Metric)] = struct{}{}
|
||||
rightSigs[sigfr(rs.Metric)] = struct{}{}
|
||||
}
|
||||
|
||||
for _, ls := range lhs {
|
||||
if _, ok := rightSigs[sigf(ls.Metric)]; !ok {
|
||||
if _, ok := rightSigs[sigfl(ls.Metric)]; !ok {
|
||||
enh.out = append(enh.out, ls)
|
||||
}
|
||||
}
|
||||
|
@ -1582,12 +1585,14 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
|
|||
if matching.Card == CardManyToMany {
|
||||
panic("many-to-many only allowed for set operators")
|
||||
}
|
||||
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
|
||||
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
|
||||
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
|
||||
|
||||
// The control flow below handles one-to-one or many-to-one matching.
|
||||
// For one-to-many, swap sidedness and account for the swap when calculating
|
||||
// values.
|
||||
if matching.Card == CardOneToMany {
|
||||
sigfl, sigfr = sigfr, sigfl
|
||||
lhs, rhs = rhs, lhs
|
||||
}
|
||||
|
||||
|
@ -1603,7 +1608,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
|
|||
|
||||
// Add all rhs samples to a map so we can easily find matches later.
|
||||
for _, rs := range rhs {
|
||||
sig := sigf(rs.Metric)
|
||||
sig := sigfr(rs.Metric)
|
||||
// The rhs is guaranteed to be the 'one' side. Having multiple samples
|
||||
// with the same signature means that the matching is many-to-many.
|
||||
if duplSample, found := rightSigs[sig]; found {
|
||||
|
@ -1612,7 +1617,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
|
|||
if matching.Card == CardOneToMany {
|
||||
oneSide = "left"
|
||||
}
|
||||
matchedLabels := rs.Metric.MatchLabels(matching.On, matching.MatchingLabels...)
|
||||
matchedLabels := rs.Metric.MatchLabels(matching.On, justNames(matching.MatchingLabels)...)
|
||||
// Many-to-many matching not allowed.
|
||||
ev.errorf("found duplicate series for the match group %s on the %s hand-side of the operation: [%s, %s]"+
|
||||
";many-to-many matching not allowed: matching labels must be unique on one side", matchedLabels.String(), oneSide, rs.Metric.String(), duplSample.Metric.String())
|
||||
|
@ -1634,7 +1639,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
|
|||
// For all lhs samples find a respective rhs sample and perform
|
||||
// the binary operation.
|
||||
for _, ls := range lhs {
|
||||
sig := sigf(ls.Metric)
|
||||
sig := sigfl(ls.Metric)
|
||||
|
||||
rs, found := rightSigs[sig] // Look for a match in the rhs Vector.
|
||||
if !found {
|
||||
|
@ -1730,14 +1735,14 @@ func resultMetric(lhs, rhs labels.Labels, op ItemType, matching *VectorMatching,
|
|||
Outer:
|
||||
for _, l := range lhs {
|
||||
for _, n := range matching.MatchingLabels {
|
||||
if l.Name == n {
|
||||
if l.Name == n.Name {
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
lb.Del(l.Name)
|
||||
}
|
||||
} else {
|
||||
lb.Del(matching.MatchingLabels...)
|
||||
lb.Del(justNames(matching.MatchingLabels)...)
|
||||
}
|
||||
}
|
||||
for _, ln := range matching.Include {
|
||||
|
|
|
@ -129,11 +129,13 @@ START_METRIC_SELECTOR
|
|||
%type <matchers> label_match_list
|
||||
%type <matcher> label_matcher
|
||||
|
||||
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op
|
||||
%type <item> aggregate_op match_op maybe_label metric_identifier unary_op
|
||||
|
||||
%type <matcher> grouping_label
|
||||
|
||||
%type <labels> label_set label_set_list metric
|
||||
%type <label> label_set_item
|
||||
%type <strings> grouping_label_list grouping_labels maybe_grouping_labels
|
||||
%type <matchers> grouping_label_list grouping_labels maybe_grouping_labels
|
||||
%type <series> series_item series_values
|
||||
%type <uint> uint
|
||||
%type <float> number series_value signed_number
|
||||
|
@ -206,13 +208,13 @@ aggregate_modifier:
|
|||
BY grouping_labels
|
||||
{
|
||||
$$ = &AggregateExpr{
|
||||
Grouping: $2,
|
||||
Grouping: justNames($2),
|
||||
}
|
||||
}
|
||||
| WITHOUT grouping_labels
|
||||
{
|
||||
$$ = &AggregateExpr{
|
||||
Grouping: $2,
|
||||
Grouping: justNames($2),
|
||||
Without: true,
|
||||
}
|
||||
}
|
||||
|
@ -276,13 +278,13 @@ group_modifiers: bool_modifier /* empty */
|
|||
{
|
||||
$$ = $1
|
||||
$$.(*BinaryExpr).VectorMatching.Card = CardManyToOne
|
||||
$$.(*BinaryExpr).VectorMatching.Include = $3
|
||||
$$.(*BinaryExpr).VectorMatching.Include = justNames($3)
|
||||
}
|
||||
| on_or_ignoring GROUP_RIGHT maybe_grouping_labels
|
||||
{
|
||||
$$ = $1
|
||||
$$.(*BinaryExpr).VectorMatching.Card = CardOneToMany
|
||||
$$.(*BinaryExpr).VectorMatching.Include = $3
|
||||
$$.(*BinaryExpr).VectorMatching.Include = justNames($3)
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -292,7 +294,7 @@ grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
|
|||
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
|
||||
{ $$ = $2 }
|
||||
| LEFT_PAREN RIGHT_PAREN
|
||||
{ $$ = []string{} }
|
||||
{ $$ = []*labels.Matcher{} }
|
||||
| error
|
||||
{ yylex.(*parser).unexpected("grouping opts", "\"(\""); $$ = nil }
|
||||
;
|
||||
|
@ -300,9 +302,9 @@ grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
|
|||
|
||||
grouping_label_list:
|
||||
grouping_label_list COMMA grouping_label
|
||||
{ $$ = append($1, $3.Val) }
|
||||
{ $$ = append($1, $3) }
|
||||
| grouping_label
|
||||
{ $$ = []string{$1.Val} }
|
||||
{ $$ = []*labels.Matcher{$1} }
|
||||
| grouping_label_list error
|
||||
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \")\""); $$ = $1 }
|
||||
;
|
||||
|
@ -312,10 +314,20 @@ grouping_label : maybe_label
|
|||
if !isLabel($1.Val) {
|
||||
yylex.(*parser).unexpected("grouping opts", "label")
|
||||
}
|
||||
$$ = $1
|
||||
$$ = yylex.(*parser).newLabelMatcher($1, Item{Typ:EQL}, $1.Quote());
|
||||
}
|
||||
| maybe_label EQL maybe_label
|
||||
{
|
||||
if !isLabel($1.Val) {
|
||||
yylex.(*parser).unexpected("grouping opts", "label")
|
||||
}
|
||||
if !isLabel($3.Val) {
|
||||
yylex.(*parser).unexpected("grouping opts", "label")
|
||||
}
|
||||
$$ = yylex.(*parser).newLabelMatcher($1, Item{Typ:EQL}, $3.Quote());
|
||||
}
|
||||
| error
|
||||
{ yylex.(*parser).unexpected("grouping opts", "label"); $$ = Item{} }
|
||||
{ yylex.(*parser).unexpected("grouping opts", "label"); $$ = nil }
|
||||
;
|
||||
|
||||
/*
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,7 @@ package promql
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
@ -27,6 +28,11 @@ type Item struct {
|
|||
Val string // The value of this Item.
|
||||
}
|
||||
|
||||
func (i Item) Quote() Item {
|
||||
i.Val = strconv.Quote(i.Val)
|
||||
return i
|
||||
}
|
||||
|
||||
// String returns a descriptive string for the Item.
|
||||
func (i Item) String() string {
|
||||
switch {
|
||||
|
|
|
@ -490,7 +490,7 @@ func (p *parser) checkAST(node Node) (typ ValueType) {
|
|||
|
||||
for _, l1 := range n.VectorMatching.MatchingLabels {
|
||||
for _, l2 := range n.VectorMatching.Include {
|
||||
if l1 == l2 && n.VectorMatching.On {
|
||||
if l1.Name == l2 && n.VectorMatching.On {
|
||||
p.addParseErrf(opRange(), "label %q must not occur in ON and GROUP clause at once", l1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,9 +90,9 @@ func (node *BinaryExpr) String() string {
|
|||
vm := node.VectorMatching
|
||||
if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) {
|
||||
if vm.On {
|
||||
matching = fmt.Sprintf(" on(%s)", strings.Join(vm.MatchingLabels, ", "))
|
||||
matching = fmt.Sprintf(" on(%s)", strings.Join(justNames(vm.MatchingLabels), ", "))
|
||||
} else {
|
||||
matching = fmt.Sprintf(" ignoring(%s)", strings.Join(vm.MatchingLabels, ", "))
|
||||
matching = fmt.Sprintf(" ignoring(%s)", strings.Join(justNames(vm.MatchingLabels), ", "))
|
||||
}
|
||||
if vm.Card == CardManyToOne || vm.Card == CardOneToMany {
|
||||
matching += " group_"
|
||||
|
|
Loading…
Reference in a new issue