// NewTagValuesIterator returns a new instance of TagValuesIterator. func NewTagValuesIterator(sh *Shard, opt influxql.IteratorOptions) (influxql.Iterator, error) { if opt.Condition == nil { return nil, errors.New("a condition is required") } mms, ok, err := sh.index.measurementsByExpr(opt.Condition) if err != nil { return nil, err } else if !ok { mms = sh.index.Measurements() sort.Sort(mms) } filterExpr := influxql.CloneExpr(opt.Condition) filterExpr = influxql.RewriteExpr(filterExpr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: switch e.Op { case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX: tag, ok := e.LHS.(*influxql.VarRef) if !ok || tag.Val == "name" || strings.HasPrefix(tag.Val, "_") { return nil } } } return e }) var series []*Series keys := newStringSet() for _, mm := range mms { ss, ok, err := mm.tagKeysByExpr(opt.Condition) if err != nil { return nil, err } else if !ok { keys.add(mm.TagKeys()...) } else { keys = keys.union(ss) } ids, err := mm.seriesIDsAllOrByExpr(filterExpr) if err != nil { return nil, err } for _, id := range ids { series = append(series, mm.SeriesByID(id)) } } return &tagValuesIterator{ series: series, keys: keys.list(), fields: opt.Aux, }, nil }
// Ensure an Expr can be rewritten handling nils. func TestRewriteExpr(t *testing.T) { expr := MustParseExpr(`(time > 1 AND time < 10) OR foo = 2`) // Remove all time expressions. act := influxql.RewriteExpr(expr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: if lhs, ok := e.LHS.(*influxql.VarRef); ok && lhs.Val == "time" { return nil } } return e }) // Verify that everything is flipped. if act := act.String(); act != `foo = 2` { t.Fatalf("unexpected result: %s", act) } }
func (s *Store) TagValues(database string, cond influxql.Expr) ([]TagValues, error) { if cond == nil { return nil, errors.New("a condition is required") } dbi := s.DatabaseIndex(database) if dbi == nil { return nil, nil } measurementExpr := influxql.CloneExpr(cond) measurementExpr = influxql.Reduce(influxql.RewriteExpr(measurementExpr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: switch e.Op { case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX: tag, ok := e.LHS.(*influxql.VarRef) if !ok || tag.Val != "_name" { return nil } } } return e }), nil) mms, ok, err := dbi.MeasurementsByExpr(measurementExpr) if err != nil { return nil, err } else if !ok { mms = dbi.Measurements() sort.Sort(mms) } // If there are no measurements, return immediately. if len(mms) == 0 { return nil, nil } filterExpr := influxql.CloneExpr(cond) filterExpr = influxql.Reduce(influxql.RewriteExpr(filterExpr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: switch e.Op { case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX: tag, ok := e.LHS.(*influxql.VarRef) if !ok || strings.HasPrefix(tag.Val, "_") { return nil } } } return e }), nil) tagValues := make([]TagValues, len(mms)) for i, mm := range mms { tagValues[i].Measurement = mm.Name ids, err := mm.SeriesIDsAllOrByExpr(filterExpr) if err != nil { return nil, err } ss := mm.SeriesByIDSlice(ids) // Determine a list of keys from condition. keySet, ok, err := mm.TagKeysByExpr(cond) if err != nil { return nil, err } // Loop over all keys for each series. m := make(map[KeyValue]struct{}, len(ss)) for _, series := range ss { for key, value := range series.Tags { if !ok { // nop } else if _, exists := keySet[key]; !exists { continue } m[KeyValue{key, value}] = struct{}{} } } // Return an empty slice if there are no key/value matches. if len(m) == 0 { continue } // Sort key/value set. a := make([]KeyValue, 0, len(m)) for kv := range m { a = append(a, kv) } sort.Sort(KeyValues(a)) tagValues[i].Values = a } return tagValues, nil }
func (e *StatementExecutor) executeShowTagValues(stmt *influxql.SelectStatement, ctx *influxql.ExecutionContext, store LocalTSDBStore) error { if stmt.Condition == nil { return errors.New("a condition is required") } source := stmt.Sources[0].(*influxql.Measurement) index := store.DatabaseIndex(source.Database) if index == nil { ctx.Results <- &influxql.Result{StatementID: ctx.StatementID, Series: make([]*models.Row, 0)} return nil } measurementExpr := influxql.CloneExpr(stmt.Condition) measurementExpr = influxql.Reduce(influxql.RewriteExpr(measurementExpr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: switch e.Op { case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX: tag, ok := e.LHS.(*influxql.VarRef) if !ok || tag.Val != "_name" { return nil } } } return e }), nil) mms, ok, err := index.MeasurementsByExpr(measurementExpr) if err != nil { return err } else if !ok { mms = index.Measurements() sort.Sort(mms) } // If there are no measurements, return immediately. if len(mms) == 0 { ctx.Results <- &influxql.Result{StatementID: ctx.StatementID, Series: make([]*models.Row, 0)} return nil } filterExpr := influxql.CloneExpr(stmt.Condition) filterExpr = influxql.Reduce(influxql.RewriteExpr(filterExpr, func(e influxql.Expr) influxql.Expr { switch e := e.(type) { case *influxql.BinaryExpr: switch e.Op { case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX: tag, ok := e.LHS.(*influxql.VarRef) if !ok || strings.HasPrefix(tag.Val, "_") { return nil } } } return e }), nil) var emitted bool columns := stmt.ColumnNames() for _, mm := range mms { ids, err := mm.SeriesIDsAllOrByExpr(filterExpr) if err != nil { return err } ss := mm.SeriesByIDSlice(ids) // Determine a list of keys from condition. keySet, ok, err := mm.TagKeysByExpr(stmt.Condition) if err != nil { return err } // Loop over all keys for each series. m := make(map[keyValue]struct{}, len(ss)) for _, series := range ss { for key, value := range series.Tags { if !ok { // nop } else if _, exists := keySet[key]; !exists { continue } m[keyValue{key, value}] = struct{}{} } } // Move to next series if no key/values match. if len(m) == 0 { continue } // Sort key/value set. a := make([]keyValue, 0, len(m)) for kv := range m { a = append(a, kv) } sort.Sort(keyValues(a)) // Convert to result values. slab := make([]interface{}, len(a)*2) values := make([][]interface{}, len(a)) for i, elem := range a { slab[i*2], slab[i*2+1] = elem.key, elem.value values[i] = slab[i*2 : i*2+2] } // Send result to client. ctx.Results <- &influxql.Result{ StatementID: ctx.StatementID, Series: []*models.Row{&models.Row{ Name: mm.Name, Columns: columns, Values: values, }}, } emitted = true } // Always emit at least one row. if !emitted { ctx.Results <- &influxql.Result{StatementID: ctx.StatementID, Series: make([]*models.Row, 0)} } return nil }