influxdb/tsdb/mapper.go

152 lines
4.6 KiB
Go

package tsdb
import (
"encoding/json"
)
// Mapper is the interface all Mapper types must implement.
type Mapper interface {
Open() error
TagSets() []string
Fields() []string
NextChunk() (interface{}, error)
Close()
}
// StatefulMapper encapsulates a Mapper and some state that the executor needs to
// track for that mapper.
type StatefulMapper struct {
Mapper
bufferedChunk *MapperOutput // Last read chunk.
drained bool
}
// NextChunk wraps a RawMapper and some state.
func (sm *StatefulMapper) NextChunk() (*MapperOutput, error) {
c, err := sm.Mapper.NextChunk()
if err != nil {
return nil, err
}
chunk, ok := c.(*MapperOutput)
if !ok {
if chunk == interface{}(nil) {
return nil, nil
}
}
return chunk, nil
}
// MapperValue is a complex type, which can encapsulate data from both raw and aggregate
// mappers. This currently allows marshalling and network system to remain simpler. For
// aggregate output Time is ignored, and actual Time-Value pairs are contained soley
// within the Value field.
type MapperValue struct {
Time int64 `json:"time,omitempty"` // Ignored for aggregate output.
Value interface{} `json:"value,omitempty"` // For aggregate, contains interval time multiple values.
Tags map[string]string `json:"tags,omitempty"` // Meta tags for results
}
// MapperValueJSON is the JSON-encoded representation of MapperValue. Because MapperValue is
// a complex type, custom JSON encoding is required so that none of the types contained within
// a MapperValue are "lost", and so the data are encoded as byte slices where necessary.
type MapperValueJSON struct {
Time int64 `json:"time,omitempty"`
RawData []byte `json:"rdata,omitempty"`
AggData [][]byte `json:"adata,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
}
// MarshalJSON returns the JSON-encoded representation of a MapperValue.
func (mv *MapperValue) MarshalJSON() ([]byte, error) {
o := &MapperValueJSON{
Time: mv.Time,
AggData: make([][]byte, 0),
Tags: mv.Tags,
}
o.Time = mv.Time
o.Tags = mv.Tags
if values, ok := mv.Value.([]interface{}); ok {
// Value contain a slice of more values. This happens only with
// aggregate output.
for _, v := range values {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
o.AggData = append(o.AggData, b)
}
} else {
// If must be raw output, so just marshal the single value.
b, err := json.Marshal(mv.Value)
if err != nil {
return nil, err
}
o.RawData = b
}
return json.Marshal(o)
}
type MapperValues []*MapperValue
func (a MapperValues) Len() int { return len(a) }
func (a MapperValues) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a MapperValues) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type MapperOutput struct {
Name string `json:"name,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
Fields []string `json:"fields,omitempty"` // Field names of returned data.
Values []*MapperValue `json:"values,omitempty"` // For aggregates contains a single value at [0]
CursorKey string `json:"cursorkey,omitempty"` // Tagset-based key for the source cursor. Cached for performance reasons.
}
// MapperOutputJSON is the JSON-encoded representation of MapperOutput. The query data is represented
// as a raw JSON message, so decode is delayed, and can proceed in a custom manner.
type MapperOutputJSON struct {
Name string `json:"name,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
Fields []string `json:"fields,omitempty"` // Field names of returned data.
CursorKey string `json:"cursorkey,omitempty"` // Tagset-based key for the source cursor.
Values json.RawMessage `json:"values,omitempty"`
}
// MarshalJSON returns the JSON-encoded representation of a MapperOutput.
func (mo *MapperOutput) MarshalJSON() ([]byte, error) {
o := &MapperOutputJSON{
Name: mo.Name,
Tags: mo.Tags,
Fields: mo.Fields,
CursorKey: mo.CursorKey,
}
data, err := json.Marshal(mo.Values)
if err != nil {
return nil, err
}
o.Values = data
return json.Marshal(o)
}
func (mo *MapperOutput) key() string {
return mo.CursorKey
}
// uniqueStrings returns a slice of unique strings from all lists in a.
func uniqueStrings(a ...[]string) []string {
// Calculate unique set of strings.
m := make(map[string]struct{})
for _, strs := range a {
for _, str := range strs {
m[str] = struct{}{}
}
}
// Convert back to slice.
result := make([]string, 0, len(m))
for k := range m {
result = append(result, k)
}
return result
}