refactor map functions to use list of values
This commit changes `tsdb.mapFunc` to use `tsdb.MapInput` instead of an iterator. This will make it easier and faster to pass blocks of values from the new storage engine into the engine.pull/4264/head
parent
f79377b488
commit
343dd23ee7
|
@ -27,6 +27,7 @@
|
||||||
- [#4222](https://github.com/influxdb/influxdb/pull/4222): Graphite TCP connections should not block shutdown
|
- [#4222](https://github.com/influxdb/influxdb/pull/4222): Graphite TCP connections should not block shutdown
|
||||||
- [#4180](https://github.com/influxdb/influxdb/pull/4180): Cursor & SelectMapper Refactor
|
- [#4180](https://github.com/influxdb/influxdb/pull/4180): Cursor & SelectMapper Refactor
|
||||||
- [#1577](https://github.com/influxdb/influxdb/issues/1577): selectors (e.g. min, max, first, last) should have equivalents to return the actual point
|
- [#1577](https://github.com/influxdb/influxdb/issues/1577): selectors (e.g. min, max, first, last) should have equivalents to return the actual point
|
||||||
|
- [#4264](https://github.com/influxdb/influxdb/issues/4264): Refactor map functions to use list of values
|
||||||
|
|
||||||
## v0.9.4 [2015-09-14]
|
## v0.9.4 [2015-09-14]
|
||||||
|
|
||||||
|
|
|
@ -2573,6 +2573,7 @@ func TestServer_Query_AggregateSelectors(t *testing.T) {
|
||||||
t.Logf("SKIP:: %s", query.name)
|
t.Logf("SKIP:: %s", query.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := query.Execute(s); err != nil {
|
if err := query.Execute(s); err != nil {
|
||||||
t.Error(query.Error(err))
|
t.Error(query.Error(err))
|
||||||
} else if !query.success() {
|
} else if !query.success() {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"github.com/influxdb/influxdb/influxql"
|
"github.com/influxdb/influxdb/influxql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// iterator represents a forward-only iterator over a set of points.
|
// Iterator represents a forward-only iterator over a set of points.
|
||||||
// These are used by the mapFunctions in this file
|
// These are used by the mapFunctions in this file
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
Next() (time int64, value interface{})
|
Next() (time int64, value interface{})
|
||||||
|
@ -28,9 +28,26 @@ type Iterator interface {
|
||||||
TMin() int64
|
TMin() int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MapInput struct {
|
||||||
|
TMin int64
|
||||||
|
Items []MapItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type MapItem struct {
|
||||||
|
Timestamp int64
|
||||||
|
Value interface{}
|
||||||
|
|
||||||
|
// TODO(benbjohnson):
|
||||||
|
// Move fields and tags up to MapInput. Currently the engine combines
|
||||||
|
// multiple series together during processing. This needs to be fixed so
|
||||||
|
// that each map function only operates on a single series at a time instead.
|
||||||
|
Fields map[string]interface{}
|
||||||
|
Tags map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
// mapFunc represents a function used for mapping over a sequential series of data.
|
// mapFunc represents a function used for mapping over a sequential series of data.
|
||||||
// The iterator represents a single group by interval
|
// The iterator represents a single group by interval
|
||||||
type mapFunc func(Iterator) interface{}
|
type mapFunc func(*MapInput) interface{}
|
||||||
|
|
||||||
// reduceFunc represents a function used for reducing mapper output.
|
// reduceFunc represents a function used for reducing mapper output.
|
||||||
type reduceFunc func([]interface{}) interface{}
|
type reduceFunc func([]interface{}) interface{}
|
||||||
|
@ -67,24 +84,24 @@ func initializeMapFunc(c *influxql.Call) (mapFunc, error) {
|
||||||
case "median":
|
case "median":
|
||||||
return MapStddev, nil
|
return MapStddev, nil
|
||||||
case "min":
|
case "min":
|
||||||
return func(itr Iterator) interface{} {
|
return func(input *MapInput) interface{} {
|
||||||
return MapMin(itr, c.Fields()[0])
|
return MapMin(input, c.Fields()[0])
|
||||||
}, nil
|
}, nil
|
||||||
case "max":
|
case "max":
|
||||||
return func(itr Iterator) interface{} {
|
return func(input *MapInput) interface{} {
|
||||||
return MapMax(itr, c.Fields()[0])
|
return MapMax(input, c.Fields()[0])
|
||||||
}, nil
|
}, nil
|
||||||
case "spread":
|
case "spread":
|
||||||
return MapSpread, nil
|
return MapSpread, nil
|
||||||
case "stddev":
|
case "stddev":
|
||||||
return MapStddev, nil
|
return MapStddev, nil
|
||||||
case "first":
|
case "first":
|
||||||
return func(itr Iterator) interface{} {
|
return func(input *MapInput) interface{} {
|
||||||
return MapFirst(itr, c.Fields()[0])
|
return MapFirst(input, c.Fields()[0])
|
||||||
}, nil
|
}, nil
|
||||||
case "last":
|
case "last":
|
||||||
return func(itr Iterator) interface{} {
|
return func(input *MapInput) interface{} {
|
||||||
return MapLast(itr, c.Fields()[0])
|
return MapLast(input, c.Fields()[0])
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case "top", "bottom":
|
case "top", "bottom":
|
||||||
|
@ -93,8 +110,8 @@ func initializeMapFunc(c *influxql.Call) (mapFunc, error) {
|
||||||
limit := int(lit.Val)
|
limit := int(lit.Val)
|
||||||
fields := topCallArgs(c)
|
fields := topCallArgs(c)
|
||||||
|
|
||||||
return func(itr Iterator) interface{} {
|
return func(input *MapInput) interface{} {
|
||||||
return MapTopBottom(itr, limit, fields, len(c.Args), c.Name)
|
return MapTopBottom(input, limit, fields, len(c.Args), c.Name)
|
||||||
}, nil
|
}, nil
|
||||||
case "percentile":
|
case "percentile":
|
||||||
return MapEcho, nil
|
return MapEcho, nil
|
||||||
|
@ -228,9 +245,9 @@ func InitializeUnmarshaller(c *influxql.Call) (UnmarshalFunc, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapCount computes the number of values in an iterator.
|
// MapCount computes the number of values in an iterator.
|
||||||
func MapCount(itr Iterator) interface{} {
|
func MapCount(input *MapInput) interface{} {
|
||||||
n := float64(0)
|
n := float64(0)
|
||||||
for k, _ := itr.Next(); k != -1; k, _ = itr.Next() {
|
for range input.Items {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
@ -253,20 +270,19 @@ func (d interfaceValues) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapDistinct computes the unique values in an iterator.
|
// MapDistinct computes the unique values in an iterator.
|
||||||
func MapDistinct(itr Iterator) interface{} {
|
func MapDistinct(input *MapInput) interface{} {
|
||||||
var index = make(map[interface{}]struct{})
|
m := make(map[interface{}]struct{})
|
||||||
|
for _, item := range input.Items {
|
||||||
for time, value := itr.Next(); time != -1; time, value = itr.Next() {
|
m[item.Value] = struct{}{}
|
||||||
index[value] = struct{}{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(index) == 0 {
|
if len(m) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
results := make(interfaceValues, len(index))
|
results := make(interfaceValues, len(m))
|
||||||
var i int
|
var i int
|
||||||
for value, _ := range index {
|
for value, _ := range m {
|
||||||
results[i] = value
|
results[i] = value
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -307,11 +323,11 @@ func ReduceDistinct(values []interface{}) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapCountDistinct computes the unique count of values in an iterator.
|
// MapCountDistinct computes the unique count of values in an iterator.
|
||||||
func MapCountDistinct(itr Iterator) interface{} {
|
func MapCountDistinct(input *MapInput) interface{} {
|
||||||
var index = make(map[interface{}]struct{})
|
var index = make(map[interface{}]struct{})
|
||||||
|
|
||||||
for time, value := itr.Next(); time != -1; time, value = itr.Next() {
|
for _, item := range input.Items {
|
||||||
index[value] = struct{}{}
|
index[item.Value] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(index) == 0 {
|
if len(index) == 0 {
|
||||||
|
@ -351,29 +367,31 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// MapSum computes the summation of values in an iterator.
|
// MapSum computes the summation of values in an iterator.
|
||||||
func MapSum(itr Iterator) interface{} {
|
func MapSum(input *MapInput) interface{} {
|
||||||
|
if len(input.Items) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
n := float64(0)
|
n := float64(0)
|
||||||
count := 0
|
|
||||||
var resultType NumberType
|
var resultType NumberType
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
count++
|
switch v := item.Value.(type) {
|
||||||
switch n1 := v.(type) {
|
|
||||||
case float64:
|
case float64:
|
||||||
n += n1
|
n += v
|
||||||
case int64:
|
case int64:
|
||||||
n += float64(n1)
|
n += float64(v)
|
||||||
resultType = Int64Type
|
resultType = Int64Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count > 0 {
|
|
||||||
switch resultType {
|
switch resultType {
|
||||||
case Float64Type:
|
case Float64Type:
|
||||||
return n
|
return n
|
||||||
case Int64Type:
|
case Int64Type:
|
||||||
return int64(n)
|
return int64(n)
|
||||||
}
|
default:
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceSum computes the sum of values for each key.
|
// ReduceSum computes the sum of values for each key.
|
||||||
|
@ -406,25 +424,23 @@ func ReduceSum(values []interface{}) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapMean computes the count and sum of values in an iterator to be combined by the reducer.
|
// MapMean computes the count and sum of values in an iterator to be combined by the reducer.
|
||||||
func MapMean(itr Iterator) interface{} {
|
func MapMean(input *MapInput) interface{} {
|
||||||
out := &meanMapOutput{}
|
if len(input.Items) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
out := &meanMapOutput{}
|
||||||
|
for _, item := range input.Items {
|
||||||
out.Count++
|
out.Count++
|
||||||
switch n1 := v.(type) {
|
switch v := item.Value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
out.Mean += (n1 - out.Mean) / float64(out.Count)
|
out.Mean += (v - out.Mean) / float64(out.Count)
|
||||||
case int64:
|
case int64:
|
||||||
out.Mean += (float64(n1) - out.Mean) / float64(out.Count)
|
out.Mean += (float64(v) - out.Mean) / float64(out.Count)
|
||||||
out.ResultType = Int64Type
|
out.ResultType = Int64Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if out.Count > 0 {
|
|
||||||
return out
|
return out
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type meanMapOutput struct {
|
type meanMapOutput struct {
|
||||||
|
@ -615,21 +631,21 @@ type minMaxMapOut struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapMin collects the values to pass to the reducer
|
// MapMin collects the values to pass to the reducer
|
||||||
func MapMin(itr Iterator, fieldName string) interface{} {
|
func MapMin(input *MapInput, fieldName string) interface{} {
|
||||||
min := &minMaxMapOut{}
|
min := &minMaxMapOut{}
|
||||||
|
|
||||||
pointsYielded := false
|
pointsYielded := false
|
||||||
var val float64
|
var val float64
|
||||||
|
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
switch n := v.(type) {
|
switch v := item.Value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
val = n
|
val = v
|
||||||
case int64:
|
case int64:
|
||||||
val = float64(n)
|
val = float64(v)
|
||||||
min.Type = Int64Type
|
min.Type = Int64Type
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
if d, t, ok := decodeValueAndNumberType(n[fieldName]); ok {
|
if d, t, ok := decodeValueAndNumberType(v[fieldName]); ok {
|
||||||
val, min.Type = d, t
|
val, min.Type = d, t
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
|
@ -638,19 +654,20 @@ func MapMin(itr Iterator, fieldName string) interface{} {
|
||||||
|
|
||||||
// Initialize min
|
// Initialize min
|
||||||
if !pointsYielded {
|
if !pointsYielded {
|
||||||
min.Time = k
|
min.Time = item.Timestamp
|
||||||
min.Val = val
|
min.Val = val
|
||||||
min.Fields = itr.Fields()
|
min.Fields = item.Fields
|
||||||
min.Tags = itr.Tags()
|
min.Tags = item.Tags
|
||||||
pointsYielded = true
|
pointsYielded = true
|
||||||
}
|
}
|
||||||
current := min.Val
|
current := min.Val
|
||||||
min.Val = math.Min(min.Val, val)
|
min.Val = math.Min(min.Val, val)
|
||||||
|
|
||||||
// Check to see if the value changed, if so, update the fields/tags
|
// Check to see if the value changed, if so, update the fields/tags
|
||||||
if current != min.Val {
|
if current != min.Val {
|
||||||
min.Time = k
|
min.Time = item.Timestamp
|
||||||
min.Fields = itr.Fields()
|
min.Fields = item.Fields
|
||||||
min.Tags = itr.Tags()
|
min.Tags = item.Tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pointsYielded {
|
if pointsYielded {
|
||||||
|
@ -724,21 +741,21 @@ func decodeValueAndNumberType(v interface{}) (float64, NumberType, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapMax collects the values to pass to the reducer
|
// MapMax collects the values to pass to the reducer
|
||||||
func MapMax(itr Iterator, fieldName string) interface{} {
|
func MapMax(input *MapInput, fieldName string) interface{} {
|
||||||
max := &minMaxMapOut{}
|
max := &minMaxMapOut{}
|
||||||
|
|
||||||
pointsYielded := false
|
pointsYielded := false
|
||||||
var val float64
|
var val float64
|
||||||
|
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
switch n := v.(type) {
|
switch v := item.Value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
val = n
|
val = v
|
||||||
case int64:
|
case int64:
|
||||||
val = float64(n)
|
val = float64(v)
|
||||||
max.Type = Int64Type
|
max.Type = Int64Type
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
if d, t, ok := decodeValueAndNumberType(n[fieldName]); ok {
|
if d, t, ok := decodeValueAndNumberType(v[fieldName]); ok {
|
||||||
val, max.Type = d, t
|
val, max.Type = d, t
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
|
@ -747,19 +764,20 @@ func MapMax(itr Iterator, fieldName string) interface{} {
|
||||||
|
|
||||||
// Initialize max
|
// Initialize max
|
||||||
if !pointsYielded {
|
if !pointsYielded {
|
||||||
max.Time = k
|
max.Time = item.Timestamp
|
||||||
max.Val = val
|
max.Val = val
|
||||||
max.Fields = itr.Fields()
|
max.Fields = item.Fields
|
||||||
max.Tags = itr.Tags()
|
max.Tags = item.Tags
|
||||||
pointsYielded = true
|
pointsYielded = true
|
||||||
}
|
}
|
||||||
current := max.Val
|
current := max.Val
|
||||||
max.Val = math.Max(max.Val, val)
|
max.Val = math.Max(max.Val, val)
|
||||||
|
|
||||||
// Check to see if the value changed, if so, update the fields/tags
|
// Check to see if the value changed, if so, update the fields/tags
|
||||||
if current != max.Val {
|
if current != max.Val {
|
||||||
max.Time = k
|
max.Time = item.Timestamp
|
||||||
max.Fields = itr.Fields()
|
max.Fields = item.Fields
|
||||||
max.Tags = itr.Tags()
|
max.Tags = item.Tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pointsYielded {
|
if pointsYielded {
|
||||||
|
@ -827,17 +845,17 @@ type spreadMapOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapSpread collects the values to pass to the reducer
|
// MapSpread collects the values to pass to the reducer
|
||||||
func MapSpread(itr Iterator) interface{} {
|
func MapSpread(input *MapInput) interface{} {
|
||||||
out := &spreadMapOutput{}
|
out := &spreadMapOutput{}
|
||||||
pointsYielded := false
|
pointsYielded := false
|
||||||
var val float64
|
var val float64
|
||||||
|
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
switch n := v.(type) {
|
switch v := item.Value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
val = n
|
val = v
|
||||||
case int64:
|
case int64:
|
||||||
val = float64(n)
|
val = float64(v)
|
||||||
out.Type = Int64Type
|
out.Type = Int64Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,19 +906,17 @@ func ReduceSpread(values []interface{}) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapStddev collects the values to pass to the reducer
|
// MapStddev collects the values to pass to the reducer
|
||||||
func MapStddev(itr Iterator) interface{} {
|
func MapStddev(input *MapInput) interface{} {
|
||||||
var values []float64
|
var a []float64
|
||||||
|
for _, item := range input.Items {
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
switch v := item.Value.(type) {
|
||||||
switch n := v.(type) {
|
|
||||||
case float64:
|
case float64:
|
||||||
values = append(values, n)
|
a = append(a, v)
|
||||||
case int64:
|
case int64:
|
||||||
values = append(values, float64(n))
|
a = append(a, float64(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return a
|
||||||
return values
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceStddev computes the stddev of values.
|
// ReduceStddev computes the stddev of values.
|
||||||
|
@ -948,29 +964,35 @@ type firstLastMapOutput struct {
|
||||||
|
|
||||||
// MapFirst collects the values to pass to the reducer
|
// MapFirst collects the values to pass to the reducer
|
||||||
// This function assumes time ordered input
|
// This function assumes time ordered input
|
||||||
func MapFirst(itr Iterator, fieldName string) interface{} {
|
func MapFirst(input *MapInput, fieldName string) interface{} {
|
||||||
var fields map[string]interface{}
|
if len(input.Items) == 0 {
|
||||||
k, v := itr.Next()
|
|
||||||
fields = itr.Fields()
|
|
||||||
if k == -1 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k, v := input.Items[0].Timestamp, input.Items[0].Value
|
||||||
|
tags := input.Items[0].Tags
|
||||||
|
fields := input.Items[0].Fields
|
||||||
if n, ok := v.(map[string]interface{}); ok {
|
if n, ok := v.(map[string]interface{}); ok {
|
||||||
v = n[fieldName]
|
v = n[fieldName]
|
||||||
}
|
}
|
||||||
|
|
||||||
nextk, nextv := itr.Next()
|
// Find greatest value at same timestamp.
|
||||||
|
for _, item := range input.Items[1:] {
|
||||||
|
nextk, nextv := item.Timestamp, item.Value
|
||||||
|
if nextk != k {
|
||||||
|
break
|
||||||
|
}
|
||||||
if n, ok := nextv.(map[string]interface{}); ok {
|
if n, ok := nextv.(map[string]interface{}); ok {
|
||||||
nextv = n[fieldName]
|
nextv = n[fieldName]
|
||||||
}
|
}
|
||||||
for nextk == k {
|
|
||||||
if greaterThan(nextv, v) {
|
if greaterThan(nextv, v) {
|
||||||
fields = itr.Fields()
|
fields = item.Fields
|
||||||
|
tags = item.Tags
|
||||||
v = nextv
|
v = nextv
|
||||||
}
|
}
|
||||||
nextk, nextv = itr.Next()
|
|
||||||
}
|
}
|
||||||
return &firstLastMapOutput{Time: k, Value: v, Fields: fields, Tags: itr.Tags()}
|
return &firstLastMapOutput{Time: k, Value: v, Fields: fields, Tags: tags}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceFirst computes the first of value.
|
// ReduceFirst computes the first of value.
|
||||||
|
@ -1014,31 +1036,33 @@ func ReduceFirst(values []interface{}) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapLast collects the values to pass to the reducer
|
// MapLast collects the values to pass to the reducer
|
||||||
func MapLast(itr Iterator, fieldName string) interface{} {
|
func MapLast(input *MapInput, fieldName string) interface{} {
|
||||||
out := &firstLastMapOutput{}
|
out := &firstLastMapOutput{}
|
||||||
pointsYielded := false
|
pointsYielded := false
|
||||||
|
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
if n, ok := v.(map[string]interface{}); ok {
|
k, v := item.Timestamp, item.Value
|
||||||
v = n[fieldName]
|
if m, ok := v.(map[string]interface{}); ok {
|
||||||
|
v = m[fieldName]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize last
|
// Initialize last
|
||||||
if !pointsYielded {
|
if !pointsYielded {
|
||||||
out.Time = k
|
out.Time = k
|
||||||
out.Value = v
|
out.Value = v
|
||||||
out.Fields = itr.Fields()
|
out.Fields = item.Fields
|
||||||
out.Tags = itr.Tags()
|
out.Tags = item.Tags
|
||||||
pointsYielded = true
|
pointsYielded = true
|
||||||
}
|
}
|
||||||
if k > out.Time {
|
if k > out.Time {
|
||||||
out.Time = k
|
out.Time = k
|
||||||
out.Value = v
|
out.Value = v
|
||||||
out.Fields = itr.Fields()
|
out.Fields = item.Fields
|
||||||
out.Tags = itr.Tags()
|
out.Tags = item.Tags
|
||||||
} else if k == out.Time && greaterThan(v, out.Value) {
|
} else if k == out.Time && greaterThan(v, out.Value) {
|
||||||
out.Value = v
|
out.Value = v
|
||||||
out.Fields = itr.Fields()
|
out.Fields = item.Fields
|
||||||
out.Tags = itr.Tags()
|
out.Tags = item.Tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pointsYielded {
|
if pointsYielded {
|
||||||
|
@ -1455,7 +1479,7 @@ func (m *mapIter) Next() (time int64, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapTopBottom emits the top/bottom data points for each group by interval
|
// MapTopBottom emits the top/bottom data points for each group by interval
|
||||||
func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callName string) interface{} {
|
func MapTopBottom(input *MapInput, limit int, fields []string, argCount int, callName string) interface{} {
|
||||||
out := positionOut{callArgs: fields}
|
out := positionOut{callArgs: fields}
|
||||||
out.points = make([]PositionPoint, 0, limit)
|
out.points = make([]PositionPoint, 0, limit)
|
||||||
minheap := topBottomMapOut{
|
minheap := topBottomMapOut{
|
||||||
|
@ -1475,9 +1499,15 @@ func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callNa
|
||||||
// For each unique permutation of the tags given,
|
// For each unique permutation of the tags given,
|
||||||
// select the max and then fall through to select top of those
|
// select the max and then fall through to select top of those
|
||||||
// points
|
// points
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
pp = PositionPoint{k, v, itr.Fields(), itr.Tags()}
|
pp = PositionPoint{
|
||||||
tags := itr.Tags()
|
Time: item.Timestamp,
|
||||||
|
Value: item.Value,
|
||||||
|
Fields: item.Fields,
|
||||||
|
Tags: item.Tags,
|
||||||
|
}
|
||||||
|
tags := item.Tags
|
||||||
|
|
||||||
// TODO in the future we need to send in fields as well
|
// TODO in the future we need to send in fields as well
|
||||||
// this will allow a user to query on both fields and tags
|
// this will allow a user to query on both fields and tags
|
||||||
// fields will take the priority over tags if there is a name collision
|
// fields will take the priority over tags if there is a name collision
|
||||||
|
@ -1487,18 +1517,24 @@ func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callNa
|
||||||
tagmap[key] = pp
|
tagmap[key] = pp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
itr = &mapIter{
|
|
||||||
m: tagmap,
|
items := make([]MapItem, 0, len(tagmap))
|
||||||
tmin: itr.TMin(),
|
for _, p := range tagmap {
|
||||||
|
items = append(items, MapItem{Timestamp: p.Time, Value: p.Value, Fields: p.Fields, Tags: p.Tags})
|
||||||
|
}
|
||||||
|
input = &MapInput{
|
||||||
|
TMin: input.TMin,
|
||||||
|
Items: items,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
|
||||||
t := k
|
for _, item := range input.Items {
|
||||||
if bt := itr.TMin(); bt > -1 {
|
t := item.Timestamp
|
||||||
t = bt
|
if input.TMin > -1 {
|
||||||
|
t = input.TMin
|
||||||
}
|
}
|
||||||
if len(out.points) < limit {
|
if len(out.points) < limit {
|
||||||
out.points = append(out.points, PositionPoint{t, v, itr.Fields(), itr.Tags()})
|
out.points = append(out.points, PositionPoint{t, item.Value, item.Fields, item.Tags})
|
||||||
if len(out.points) == limit {
|
if len(out.points) == limit {
|
||||||
heap.Init(&minheap)
|
heap.Init(&minheap)
|
||||||
}
|
}
|
||||||
|
@ -1506,12 +1542,13 @@ func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callNa
|
||||||
// we're over the limit, so find out if we're bigger than the
|
// we're over the limit, so find out if we're bigger than the
|
||||||
// smallest point in the set and eject it if we are
|
// smallest point in the set and eject it if we are
|
||||||
minval := &out.points[0]
|
minval := &out.points[0]
|
||||||
pp = PositionPoint{t, v, itr.Fields(), itr.Tags()}
|
pp = PositionPoint{t, item.Value, item.Fields, item.Tags}
|
||||||
if minheap.positionPointLess(minval, &pp) {
|
if minheap.positionPointLess(minval, &pp) {
|
||||||
minheap.insert(pp)
|
minheap.insert(pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should only happen on empty iterator.
|
// should only happen on empty iterator.
|
||||||
if len(out.points) == 0 {
|
if len(out.points) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1521,6 +1558,7 @@ func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callNa
|
||||||
// rid of another sort order.
|
// rid of another sort order.
|
||||||
heap.Init(&minheap)
|
heap.Init(&minheap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// minheap should now contain the largest/smallest values that were encountered
|
// minheap should now contain the largest/smallest values that were encountered
|
||||||
// during iteration.
|
// during iteration.
|
||||||
//
|
//
|
||||||
|
@ -1533,10 +1571,12 @@ func MapTopBottom(itr Iterator, limit int, fields []string, argCount int, callNa
|
||||||
for len(out.points) > 0 {
|
for len(out.points) > 0 {
|
||||||
p := out.points[0]
|
p := out.points[0]
|
||||||
heap.Pop(&minheap)
|
heap.Pop(&minheap)
|
||||||
|
|
||||||
// reslice so that we can get to the element just after the heap
|
// reslice so that we can get to the element just after the heap
|
||||||
endslice := out.points[:len(out.points)+1]
|
endslice := out.points[:len(out.points)+1]
|
||||||
endslice[len(endslice)-1] = p
|
endslice[len(endslice)-1] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
// the ascending order is now in the result slice
|
// the ascending order is now in the result slice
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -1589,11 +1629,10 @@ func ReduceTopBottom(values []interface{}, c *influxql.Call) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapEcho emits the data points for each group by interval
|
// MapEcho emits the data points for each group by interval
|
||||||
func MapEcho(itr Iterator) interface{} {
|
func MapEcho(input *MapInput) interface{} {
|
||||||
var values []interface{}
|
var values []interface{}
|
||||||
|
for _, item := range input.Items {
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
values = append(values, item.Value)
|
||||||
values = append(values, v)
|
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
@ -1645,11 +1684,10 @@ func IsNumeric(c *influxql.Call) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapRawQuery is for queries without aggregates
|
// MapRawQuery is for queries without aggregates
|
||||||
func MapRawQuery(itr Iterator) interface{} {
|
func MapRawQuery(input *MapInput) interface{} {
|
||||||
var values []*rawQueryMapOutput
|
var values []*rawQueryMapOutput
|
||||||
for k, v := itr.Next(); k != -1; k, v = itr.Next() {
|
for _, item := range input.Items {
|
||||||
val := &rawQueryMapOutput{k, v}
|
values = append(values, &rawQueryMapOutput{item.Timestamp, item.Value})
|
||||||
values = append(values, val)
|
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,63 +11,15 @@ import (
|
||||||
|
|
||||||
import "sort"
|
import "sort"
|
||||||
|
|
||||||
type testPoint struct {
|
// type testPoint struct {
|
||||||
seriesKey string
|
// time int64
|
||||||
time int64
|
// value interface{}
|
||||||
value interface{}
|
// fields map[string]interface{}
|
||||||
fields map[string]interface{}
|
// tags map[string]string
|
||||||
tags map[string]string
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
type testIterator struct {
|
|
||||||
values []testPoint
|
|
||||||
lastFields map[string]interface{}
|
|
||||||
lastTags map[string]string
|
|
||||||
nextFunc func() (timestamp int64, value interface{})
|
|
||||||
fieldsFunc func() map[string]interface{}
|
|
||||||
tagsFunc func() map[string]string
|
|
||||||
tMinFunc func() int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testIterator) Next() (timestamp int64, value interface{}) {
|
|
||||||
if t.nextFunc != nil {
|
|
||||||
return t.nextFunc()
|
|
||||||
}
|
|
||||||
if len(t.values) > 0 {
|
|
||||||
v := t.values[0]
|
|
||||||
t.lastFields = t.values[0].fields
|
|
||||||
t.lastTags = t.values[0].tags
|
|
||||||
t.values = t.values[1:]
|
|
||||||
return v.time, v.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testIterator) Fields() map[string]interface{} {
|
|
||||||
if t.fieldsFunc != nil {
|
|
||||||
return t.fieldsFunc()
|
|
||||||
}
|
|
||||||
return t.lastFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testIterator) Tags() map[string]string {
|
|
||||||
if t.tagsFunc != nil {
|
|
||||||
return t.tagsFunc()
|
|
||||||
}
|
|
||||||
return t.lastTags
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testIterator) TMin() int64 {
|
|
||||||
if t.tMinFunc != nil {
|
|
||||||
return t.tMinFunc()
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapMeanNoValues(t *testing.T) {
|
func TestMapMeanNoValues(t *testing.T) {
|
||||||
iter := &testIterator{}
|
if got := MapMean(&MapInput{}); got != nil {
|
||||||
if got := MapMean(iter); got != nil {
|
|
||||||
t.Errorf("output mismatch: exp nil got %v", got)
|
t.Errorf("output mismatch: exp nil got %v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,28 +27,30 @@ func TestMapMeanNoValues(t *testing.T) {
|
||||||
func TestMapMean(t *testing.T) {
|
func TestMapMean(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input []testPoint
|
input *MapInput
|
||||||
output *meanMapOutput
|
output *meanMapOutput
|
||||||
}{
|
}{
|
||||||
{ // Single point
|
{ // Single point
|
||||||
input: []testPoint{testPoint{"0", 1, 1.0, nil, nil}},
|
input: &MapInput{
|
||||||
|
Items: []MapItem{
|
||||||
|
{Timestamp: 1, Value: 1.0},
|
||||||
|
},
|
||||||
|
},
|
||||||
output: &meanMapOutput{1, 1, Float64Type},
|
output: &meanMapOutput{1, 1, Float64Type},
|
||||||
},
|
},
|
||||||
{ // Two points
|
{ // Two points
|
||||||
input: []testPoint{
|
input: &MapInput{
|
||||||
testPoint{"0", 1, 2.0, nil, nil},
|
Items: []MapItem{
|
||||||
testPoint{"0", 2, 8.0, nil, nil},
|
{Timestamp: 1, Value: float64(2.0)},
|
||||||
|
{Timestamp: 2, Value: float64(8.0)},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
output: &meanMapOutput{2, 5.0, Float64Type},
|
output: &meanMapOutput{2, 5.0, Float64Type},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
iter := &testIterator{
|
got := MapMean(test.input)
|
||||||
values: test.input,
|
|
||||||
}
|
|
||||||
|
|
||||||
got := MapMean(iter)
|
|
||||||
if got == nil {
|
if got == nil {
|
||||||
t.Fatalf("MapMean(%v): output mismatch: exp %v got %v", test.input, test.output, got)
|
t.Fatalf("MapMean(%v): output mismatch: exp %v got %v", test.input, test.output, got)
|
||||||
}
|
}
|
||||||
|
@ -154,11 +108,6 @@ func TestReducePercentileNil(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapDistinct(t *testing.T) {
|
func TestMapDistinct(t *testing.T) {
|
||||||
const ( // prove that we're ignoring seriesKey
|
|
||||||
seriesKey1 = "1"
|
|
||||||
seriesKey2 = "2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // prove that we're ignoring time
|
const ( // prove that we're ignoring time
|
||||||
timeId1 = iota + 1
|
timeId1 = iota + 1
|
||||||
timeId2
|
timeId2
|
||||||
|
@ -168,18 +117,18 @@ func TestMapDistinct(t *testing.T) {
|
||||||
timeId6
|
timeId6
|
||||||
)
|
)
|
||||||
|
|
||||||
iter := &testIterator{
|
input := &MapInput{
|
||||||
values: []testPoint{
|
Items: []MapItem{
|
||||||
{seriesKey1, timeId1, uint64(1), nil, nil},
|
{Timestamp: timeId1, Value: uint64(1)},
|
||||||
{seriesKey1, timeId2, uint64(1), nil, nil},
|
{Timestamp: timeId2, Value: uint64(1)},
|
||||||
{seriesKey1, timeId3, "1", nil, nil},
|
{Timestamp: timeId3, Value: "1"},
|
||||||
{seriesKey2, timeId4, uint64(1), nil, nil},
|
{Timestamp: timeId4, Value: uint64(1)},
|
||||||
{seriesKey2, timeId5, float64(1.0), nil, nil},
|
{Timestamp: timeId5, Value: float64(1.0)},
|
||||||
{seriesKey2, timeId6, "1", nil, nil},
|
{Timestamp: timeId6, Value: "1"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
values := MapDistinct(iter).(interfaceValues)
|
values := MapDistinct(input).(interfaceValues)
|
||||||
|
|
||||||
if exp, got := 3, len(values); exp != got {
|
if exp, got := 3, len(values); exp != got {
|
||||||
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
||||||
|
@ -199,11 +148,7 @@ func TestMapDistinct(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapDistinctNil(t *testing.T) {
|
func TestMapDistinctNil(t *testing.T) {
|
||||||
iter := &testIterator{
|
values := MapDistinct(&MapInput{})
|
||||||
values: []testPoint{},
|
|
||||||
}
|
|
||||||
|
|
||||||
values := MapDistinct(iter)
|
|
||||||
|
|
||||||
if values != nil {
|
if values != nil {
|
||||||
t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values))
|
t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values))
|
||||||
|
@ -307,11 +252,6 @@ func Test_distinctValues_Sort(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapCountDistinct(t *testing.T) {
|
func TestMapCountDistinct(t *testing.T) {
|
||||||
const ( // prove that we're ignoring seriesKey
|
|
||||||
seriesKey1 = "1"
|
|
||||||
seriesKey2 = "2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // prove that we're ignoring time
|
const ( // prove that we're ignoring time
|
||||||
timeId1 = iota + 1
|
timeId1 = iota + 1
|
||||||
timeId2
|
timeId2
|
||||||
|
@ -322,19 +262,19 @@ func TestMapCountDistinct(t *testing.T) {
|
||||||
timeId7
|
timeId7
|
||||||
)
|
)
|
||||||
|
|
||||||
iter := &testIterator{
|
input := &MapInput{
|
||||||
values: []testPoint{
|
Items: []MapItem{
|
||||||
{seriesKey1, timeId1, uint64(1), nil, nil},
|
{Timestamp: timeId1, Value: uint64(1)},
|
||||||
{seriesKey1, timeId2, uint64(1), nil, nil},
|
{Timestamp: timeId2, Value: uint64(1)},
|
||||||
{seriesKey1, timeId3, "1", nil, nil},
|
{Timestamp: timeId3, Value: "1"},
|
||||||
{seriesKey2, timeId4, uint64(1), nil, nil},
|
{Timestamp: timeId4, Value: uint64(1)},
|
||||||
{seriesKey2, timeId5, float64(1.0), nil, nil},
|
{Timestamp: timeId5, Value: float64(1.0)},
|
||||||
{seriesKey2, timeId6, "1", nil, nil},
|
{Timestamp: timeId6, Value: "1"},
|
||||||
{seriesKey2, timeId7, true, nil, nil},
|
{Timestamp: timeId7, Value: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
values := MapCountDistinct(iter).(map[interface{}]struct{})
|
values := MapCountDistinct(input).(map[interface{}]struct{})
|
||||||
|
|
||||||
if exp, got := 4, len(values); exp != got {
|
if exp, got := 4, len(values); exp != got {
|
||||||
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
||||||
|
@ -353,13 +293,7 @@ func TestMapCountDistinct(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapCountDistinctNil(t *testing.T) {
|
func TestMapCountDistinctNil(t *testing.T) {
|
||||||
iter := &testIterator{
|
if values := MapCountDistinct(&MapInput{}); values != nil {
|
||||||
values: []testPoint{},
|
|
||||||
}
|
|
||||||
|
|
||||||
values := MapCountDistinct(iter)
|
|
||||||
|
|
||||||
if values != nil {
|
|
||||||
t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values))
|
t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,284 +429,223 @@ func TestMapTopBottom(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
skip bool
|
skip bool
|
||||||
iter *testIterator
|
input *MapInput
|
||||||
exp positionOut
|
exp positionOut
|
||||||
call *influxql.Call
|
call *influxql.Call
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "top int64 - basic",
|
name: "top int64 - basic",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, int64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 20, Value: int64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
{20, int64(88), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "top int64 - basic with tag",
|
|
||||||
iter: &testIterator{
|
|
||||||
values: []testPoint{
|
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
{"", 20, int64(53), nil, map[string]string{"host": "b"}},
|
|
||||||
{"", 30, int64(88), nil, map[string]string{"host": "a"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exp: positionOut{
|
|
||||||
callArgs: []string{"host"},
|
|
||||||
points: PositionPoints{
|
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
PositionPoint{20, int64(53), nil, map[string]string{"host": "b"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "top int64 - tie on value, resolve based on time",
|
name: "top int64 - tie on value, resolve based on time",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 20, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "a"}},
|
{Timestamp: 20, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
callArgs: []string{"host"},
|
callArgs: []string{"host"},
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
{10, int64(99), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, int64(99), nil, map[string]string{"host": "a"}},
|
{20, int64(99), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "top int64 - tie on value, time, resolve based on tags",
|
|
||||||
iter: &testIterator{
|
|
||||||
values: []testPoint{
|
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "b"}},
|
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
{"", 20, int64(88), nil, map[string]string{"host": "a"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exp: positionOut{
|
|
||||||
callArgs: []string{"host"},
|
|
||||||
points: PositionPoints{
|
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "b"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "top mixed numerics - ints",
|
name: "top mixed numerics - ints",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, uint64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
{10, int64(99), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, uint64(88), nil, map[string]string{"host": "a"}},
|
{20, uint64(88), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "top mixed numerics - ints & floats",
|
name: "top mixed numerics - ints & floats",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, float64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, uint64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, float64(99), nil, map[string]string{"host": "a"}},
|
{10, float64(99), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, uint64(88), nil, map[string]string{"host": "a"}},
|
{20, uint64(88), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "top mixed numerics - ints, floats, & strings",
|
name: "top mixed numerics - ints, floats, & strings",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, float64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, "88", nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: "88", Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, float64(99), nil, map[string]string{"host": "a"}},
|
{10, float64(99), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "top bools",
|
name: "top bools",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, true, nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, true, nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, false, nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: false, Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, true, nil, map[string]string{"host": "a"}},
|
{10, true, nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{10, true, nil, map[string]string{"host": "b"}},
|
{10, true, nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bottom int64 - basic",
|
name: "bottom int64 - basic",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, int64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: int64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}},
|
{20, int64(88), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "bottom int64 - basic with tag",
|
|
||||||
iter: &testIterator{
|
|
||||||
values: []testPoint{
|
|
||||||
{"", 10, int64(20), nil, map[string]string{"host": "a"}},
|
|
||||||
{"", 20, int64(53), nil, map[string]string{"host": "b"}},
|
|
||||||
{"", 30, int64(30), nil, map[string]string{"host": "a"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exp: positionOut{
|
|
||||||
callArgs: []string{"host"},
|
|
||||||
points: PositionPoints{
|
|
||||||
PositionPoint{10, int64(20), nil, map[string]string{"host": "a"}},
|
|
||||||
PositionPoint{20, int64(53), nil, map[string]string{"host": "b"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "bottom int64 - tie on value, resolve based on time",
|
name: "bottom int64 - tie on value, resolve based on time",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 20, int64(53), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, int64(53), nil, map[string]string{"host": "a"}},
|
{Timestamp: 20, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
callArgs: []string{"host"},
|
callArgs: []string{"host"},
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(53), nil, map[string]string{"host": "a"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, int64(53), nil, map[string]string{"host": "a"}},
|
{20, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "bottom int64 - tie on value, time, resolve based on tags",
|
|
||||||
iter: &testIterator{
|
|
||||||
values: []testPoint{
|
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "b"}},
|
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
{"", 20, int64(100), nil, map[string]string{"host": "a"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exp: positionOut{
|
|
||||||
callArgs: []string{"host"},
|
|
||||||
points: PositionPoints{
|
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}},
|
|
||||||
PositionPoint{10, int64(99), nil, map[string]string{"host": "b"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "bottom mixed numerics - ints",
|
name: "bottom mixed numerics - ints",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, uint64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, uint64(88), nil, map[string]string{"host": "a"}},
|
{20, uint64(88), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bottom mixed numerics - ints & floats",
|
name: "bottom mixed numerics - ints & floats",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, int64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, float64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, uint64(88), nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: float64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, float64(53), nil, map[string]string{"host": "b"}},
|
{10, float64(53), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{20, uint64(88), nil, map[string]string{"host": "a"}},
|
{20, uint64(88), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bottom mixed numerics - ints, floats, & strings",
|
name: "bottom mixed numerics - ints, floats, & strings",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, float64(99), nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, int64(53), nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, "88", nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: "88", Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}},
|
{10, int64(53), nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{10, float64(99), nil, map[string]string{"host": "a"}},
|
{10, float64(99), nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bottom bools",
|
name: "bottom bools",
|
||||||
iter: &testIterator{
|
input: &MapInput{
|
||||||
values: []testPoint{
|
TMin: -1,
|
||||||
{"", 10, true, nil, map[string]string{"host": "a"}},
|
Items: []MapItem{
|
||||||
{"", 10, true, nil, map[string]string{"host": "b"}},
|
{Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}},
|
||||||
{"", 20, false, nil, map[string]string{"host": "a"}},
|
{Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}},
|
||||||
|
{Timestamp: 20, Value: false, Tags: map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exp: positionOut{
|
exp: positionOut{
|
||||||
points: PositionPoints{
|
points: PositionPoints{
|
||||||
PositionPoint{20, false, nil, map[string]string{"host": "a"}},
|
{20, false, nil, map[string]string{"host": "a"}},
|
||||||
PositionPoint{10, true, nil, map[string]string{"host": "a"}},
|
{10, true, nil, map[string]string{"host": "a"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}},
|
||||||
|
@ -787,7 +660,7 @@ func TestMapTopBottom(t *testing.T) {
|
||||||
limit := int(lit.Val)
|
limit := int(lit.Val)
|
||||||
fields := topCallArgs(test.call)
|
fields := topCallArgs(test.call)
|
||||||
|
|
||||||
values := MapTopBottom(test.iter, limit, fields, len(test.call.Args), test.call.Name).(PositionPoints)
|
values := MapTopBottom(test.input, limit, fields, len(test.call.Args), test.call.Name).(PositionPoints)
|
||||||
t.Logf("Test: %s", test.name)
|
t.Logf("Test: %s", test.name)
|
||||||
if exp, got := len(test.exp.points), len(values); exp != got {
|
if exp, got := len(test.exp.points), len(values); exp != got {
|
||||||
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
t.Errorf("Wrong number of values. exp %v got %v", exp, got)
|
||||||
|
|
|
@ -605,15 +605,25 @@ func (m *AggregateMapper) NextChunk() (interface{}, error) {
|
||||||
tsc.SelectFields = []string{m.fieldNames[i]}
|
tsc.SelectFields = []string{m.fieldNames[i]}
|
||||||
tsc.SelectWhereFields = uniqueStrings([]string{m.fieldNames[i]}, m.whereFields)
|
tsc.SelectWhereFields = uniqueStrings([]string{m.fieldNames[i]}, m.whereFields)
|
||||||
|
|
||||||
// Execute the map function which walks the entire interval, and aggregates the result.
|
// Build a map input from the cursor.
|
||||||
mapValue := m.mapFuncs[i](&AggregateTagSetCursor{
|
input := &MapInput{
|
||||||
cursor: tsc,
|
TMin: -1,
|
||||||
tmin: tmin,
|
}
|
||||||
stmt: m.stmt,
|
if len(m.stmt.Dimensions) > 0 && !m.stmt.HasTimeFieldSpecified() {
|
||||||
|
input.TMin = tmin
|
||||||
|
}
|
||||||
|
|
||||||
qmin: qmin,
|
for k, v := tsc.Next(qmin, qmax); k != -1; k, v = tsc.Next(qmin, qmax) {
|
||||||
qmax: qmax,
|
input.Items = append(input.Items, MapItem{
|
||||||
|
Timestamp: k,
|
||||||
|
Value: v,
|
||||||
|
Fields: tsc.Fields(),
|
||||||
|
Tags: tsc.Tags(),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the map function which walks the entire interval, and aggregates the result.
|
||||||
|
mapValue := m.mapFuncs[i](input)
|
||||||
output.Values[0].Value = append(output.Values[0].Value.([]interface{}), mapValue)
|
output.Values[0].Value = append(output.Values[0].Value.([]interface{}), mapValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,40 +645,6 @@ func (m *AggregateMapper) nextInterval() (start, end int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregateTagSetCursor wraps a standard tagSetCursor, such that the values it emits are aggregated by intervals.
|
|
||||||
type AggregateTagSetCursor struct {
|
|
||||||
cursor *TagSetCursor
|
|
||||||
qmin int64
|
|
||||||
qmax int64
|
|
||||||
|
|
||||||
tmin int64
|
|
||||||
stmt *influxql.SelectStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the next aggregate value for the cursor.
|
|
||||||
func (a *AggregateTagSetCursor) Next() (time int64, value interface{}) {
|
|
||||||
return a.cursor.Next(a.qmin, a.qmax)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns the current fields for the cursor
|
|
||||||
func (a *AggregateTagSetCursor) Fields() map[string]interface{} {
|
|
||||||
return a.cursor.Fields()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags returns the current tags for the cursor
|
|
||||||
func (a *AggregateTagSetCursor) Tags() map[string]string { return a.cursor.Tags() }
|
|
||||||
|
|
||||||
// TMin returns the current floor time for the bucket being worked on
|
|
||||||
func (a *AggregateTagSetCursor) TMin() int64 {
|
|
||||||
if len(a.stmt.Dimensions) == 0 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if !a.stmt.HasTimeFieldSpecified() {
|
|
||||||
return a.tmin
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// uniqueStrings returns a slice of unique strings from all lists in a.
|
// uniqueStrings returns a slice of unique strings from all lists in a.
|
||||||
func uniqueStrings(a ...[]string) []string {
|
func uniqueStrings(a ...[]string) []string {
|
||||||
// Calculate unique set of strings.
|
// Calculate unique set of strings.
|
||||||
|
|
Loading…
Reference in New Issue