// NewFloatCursorIterator returns a new instance of FloatCursorIterator. func NewFloatCursorIterator(name string, tagMap map[string]string, cur Cursor, opt influxql.IteratorOptions) *FloatCursorIterator { // Extract variable reference if available. var ref *influxql.VarRef if opt.Expr != nil { ref = opt.Expr.(*influxql.VarRef) } // Only allocate aux values if we have any requested. var aux []interface{} if len(opt.Aux) > 0 { aux = make([]interface{}, len(opt.Aux)) } // Convert to influxql tags. tags := influxql.NewTags(tagMap) // Determine initial seek position based on sort direction. seek := opt.StartTime if !opt.Ascending { seek = opt.EndTime } return &FloatCursorIterator{ point: influxql.FloatPoint{ Name: name, Tags: tags.Subset(opt.Dimensions), Aux: aux, }, opt: opt, ref: ref, tags: tags, cursor: newBufCursor(cur, seek), } }
// ParseTags returns an instance of Tags for a comma-delimited list of key/values. func ParseTags(s string) influxql.Tags { m := make(map[string]string) for _, kv := range strings.Split(s, ",") { a := strings.Split(kv, "=") m[a[0]] = a[1] } return influxql.NewTags(m) }
// Ensure that a subset can be created from a tag set. func TestTags_Subset(t *testing.T) { tags := influxql.NewTags(map[string]string{"a": "0", "b": "1", "c": "2"}) subset := tags.Subset([]string{"b", "c", "d"}) if keys := subset.Keys(); !reflect.DeepEqual(keys, []string{"b", "c", "d"}) { t.Fatalf("unexpected keys: %+v", keys) } else if v := subset.Value("a"); v != "" { t.Fatalf("unexpected 'a' value: %s", v) } else if v := subset.Value("b"); v != "1" { t.Fatalf("unexpected 'b' value: %s", v) } else if v := subset.Value("c"); v != "2" { t.Fatalf("unexpected 'c' value: %s", v) } else if v := subset.Value("d"); v != "" { t.Fatalf("unexpected 'd' value: %s", v) } }
// createVarRefSeriesIterator creates an iterator for a variable reference for a series. func (e *Engine) createVarRefSeriesIterator(ref *influxql.VarRef, mm *tsdb.Measurement, seriesKey string, t *influxql.TagSet, filter influxql.Expr, conditionFields []string, opt influxql.IteratorOptions) (influxql.Iterator, error) { tags := influxql.NewTags(e.index.TagsForSeries(seriesKey)) // Create options specific for this series. itrOpt := opt itrOpt.Condition = filter // Build auxilary cursors. // Tag values should be returned if the field doesn't exist. var aux []cursorAt if len(opt.Aux) > 0 { aux = make([]cursorAt, len(opt.Aux)) for i := range aux { // Create cursor from field. cur := e.buildCursor(mm.Name, seriesKey, opt.Aux[i], opt) if cur != nil { aux[i] = newBufCursor(cur, opt.Ascending) continue } // If field doesn't exist, use the tag value. // However, if the tag value is blank then return a null. if v := tags.Value(opt.Aux[i]); v == "" { aux[i] = &stringNilLiteralCursor{} } else { aux[i] = &stringLiteralCursor{value: v} } } } // Build conditional field cursors. // If a conditional field doesn't exist then ignore the series. var conds []*bufCursor if len(conditionFields) > 0 { conds = make([]*bufCursor, len(conditionFields)) for i := range conds { cur := e.buildCursor(mm.Name, seriesKey, conditionFields[i], opt) if cur == nil { return nil, nil } conds[i] = newBufCursor(cur, opt.Ascending) } } // Limit tags to only the dimensions selected. tags = tags.Subset(opt.Dimensions) // If it's only auxiliary fields then it doesn't matter what type of iterator we use. if ref == nil { return newFloatIterator(mm.Name, tags, itrOpt, nil, aux, conds, conditionFields), nil } // Build main cursor. cur := e.buildCursor(mm.Name, seriesKey, ref.Val, opt) // If the field doesn't exist then don't build an iterator. if cur == nil { return nil, nil } switch cur := cur.(type) { case floatCursor: return newFloatIterator(mm.Name, tags, itrOpt, cur, aux, conds, conditionFields), nil case integerCursor: return newIntegerIterator(mm.Name, tags, itrOpt, cur, aux, conds, conditionFields), nil case stringCursor: return newStringIterator(mm.Name, tags, itrOpt, cur, aux, conds, conditionFields), nil case booleanCursor: return newBooleanIterator(mm.Name, tags, itrOpt, cur, aux, conds, conditionFields), nil default: panic("unreachable") } }
func (e *Engine) SeriesKeys(opt influxql.IteratorOptions) (influxql.SeriesList, error) { seriesList := influxql.SeriesList{} mms := tsdb.Measurements(e.index.MeasurementsByName(influxql.Sources(opt.Sources).Names())) for _, mm := range mms { // Determine tagsets for this measurement based on dimensions and filters. tagSets, err := mm.TagSets(opt.Dimensions, opt.Condition) if err != nil { return nil, err } // Calculate tag sets and apply SLIMIT/SOFFSET. tagSets = influxql.LimitTagSets(tagSets, opt.SLimit, opt.SOffset) for _, t := range tagSets { tagMap := make(map[string]string) for k, v := range t.Tags { if v == "" { continue } tagMap[k] = v } tags := influxql.NewTags(tagMap) series := influxql.Series{ Name: mm.Name, Tags: tags, Aux: make([]influxql.DataType, len(opt.Aux)), } // Determine the aux field types. for _, seriesKey := range t.SeriesKeys { tags := influxql.NewTags(e.index.TagsForSeries(seriesKey)) for i, field := range opt.Aux { typ := func() influxql.DataType { mf := e.measurementFields[mm.Name] if mf == nil { return influxql.Unknown } f := mf.Field(field) if f == nil { return influxql.Unknown } return f.Type }() if typ == influxql.Unknown { if v := tags.Value(field); v != "" { // All tags are strings. typ = influxql.String } } if typ != influxql.Unknown { if series.Aux[i] == influxql.Unknown || typ < series.Aux[i] { series.Aux[i] = typ } } } } seriesList = append(seriesList, series) } } return seriesList, nil }
// Ensure that tags can return a unique id. func TestTags_ID(t *testing.T) { tags := influxql.NewTags(map[string]string{"foo": "bar", "baz": "bat"}) if id := tags.ID(); id != "baz\x00foo\x00bat\x00bar" { t.Fatalf("unexpected id: %q", id) } }