LIMIT/OFFSET

pull/5196/head
Ben Johnson 2015-12-23 21:42:10 -07:00
parent 036382ee20
commit 60b051ee88
9 changed files with 699 additions and 188 deletions

View File

@ -4373,7 +4373,7 @@ func TestServer_Query_LimitAndOffset(t *testing.T) {
&Query{
name: "limit - offset higher than number of points",
command: `select foo from "limited" LIMIT 2 OFFSET 20`,
exp: `{"results":[{"series":[{"name":"limited","columns":["time","foo"]}]}]}`,
exp: `{"results":[{}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
@ -4406,18 +4406,6 @@ func TestServer_Query_LimitAndOffset(t *testing.T) {
exp: `{"results":[{}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: "limit higher than the number of data points should error",
command: `select mean(foo) from "limited" where time > '2000-01-01T00:00:00Z' group by time(1s), * fill(0) limit 2147483647`,
exp: `{"results":[{"error":"too many points in the group by interval. maybe you forgot to specify a where time clause?"}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: "limit1 higher than MaxGroupBy but the number of data points is less than MaxGroupBy",
command: `select mean(foo) from "limited" where time >= '2009-11-10T23:00:02Z' and time < '2009-11-10T23:00:03Z' group by time(1s), * fill(0) limit 2147483647`,
exp: `{"results":[{"series":[{"name":"limited","tags":{"tennant":"paul"},"columns":["time","mean"],"values":[["2009-11-10T23:00:02Z",2]]},{"name":"limited","tags":{"tennant":"todd"},"columns":["time","mean"],"values":[["2009-11-10T23:00:02Z",0]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
}...)
for i, query := range test.queries {

View File

@ -4,6 +4,7 @@
package influxql
import (
"container/heap"
"math"
"sort"
"sync"
@ -213,6 +214,107 @@ func (itr *floatMergeIterator) Next() *FloatPoint {
return nil
}
// newFloatSortedMergeIterator returns an instance of floatSortedMergeIterator.
func newFloatSortedMergeIterator(inputs []FloatIterator, opt IteratorOptions) Iterator {
itr := &floatSortedMergeIterator{
inputs: newBufFloatIterators(inputs),
heap: make(floatHeap, 0, len(inputs)),
opt: opt,
}
// Initialize heap.
for _, input := range inputs {
// Read next point.
p := input.Next()
if p == nil {
continue
}
// Append to the heap.
itr.heap = append(itr.heap, &floatHeapItem{point: p, itr: input, ascending: opt.Ascending})
}
heap.Init(&itr.heap)
return itr
}
// floatSortedMergeIterator is an iterator that sorts and merges multiple iterators into one.
type floatSortedMergeIterator struct {
inputs bufFloatIterators
opt IteratorOptions
heap floatHeap
}
// Close closes the underlying iterators.
func (itr *floatSortedMergeIterator) Close() error { return itr.inputs.Close() }
// Next returns the next points from the iterator.
func (itr *floatSortedMergeIterator) Next() *FloatPoint { return itr.pop() }
// pop returns the next point from the heap.
// Reads the next point from item's cursor and puts it back on the heap.
func (itr *floatSortedMergeIterator) pop() *FloatPoint {
if len(itr.heap) == 0 {
return nil
}
// Read the next item from the heap.
item := heap.Pop(&itr.heap).(*floatHeapItem)
// Copy the point for return.
p := item.point.Clone()
// Read the next item from the cursor. Push back to heap if one exists.
if item.point = item.itr.Next(); item.point != nil {
heap.Push(&itr.heap, item)
}
return p
}
// floatHeap represents a heap of floatHeapItems.
type floatHeap []*floatHeapItem
func (h floatHeap) Len() int { return len(h) }
func (h floatHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h floatHeap) Less(i, j int) bool {
x, y := h[i].point, h[j].point
if h[i].ascending {
if x.Name != y.Name {
return x.Name < y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() < y.Tags.ID()
}
return x.Time < y.Time
}
if x.Name != y.Name {
return x.Name > y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() > y.Tags.ID()
}
return x.Time > y.Time
}
func (h *floatHeap) Push(x interface{}) {
*h = append(*h, x.(*floatHeapItem))
}
func (h *floatHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
type floatHeapItem struct {
point *FloatPoint
itr FloatIterator
ascending bool
}
// floatJoinIterator represents a join iterator that processes float values.
type floatJoinIterator struct {
input FloatIterator
@ -706,6 +808,107 @@ func (itr *stringMergeIterator) Next() *StringPoint {
return nil
}
// newStringSortedMergeIterator returns an instance of stringSortedMergeIterator.
func newStringSortedMergeIterator(inputs []StringIterator, opt IteratorOptions) Iterator {
itr := &stringSortedMergeIterator{
inputs: newBufStringIterators(inputs),
heap: make(stringHeap, 0, len(inputs)),
opt: opt,
}
// Initialize heap.
for _, input := range inputs {
// Read next point.
p := input.Next()
if p == nil {
continue
}
// Append to the heap.
itr.heap = append(itr.heap, &stringHeapItem{point: p, itr: input, ascending: opt.Ascending})
}
heap.Init(&itr.heap)
return itr
}
// stringSortedMergeIterator is an iterator that sorts and merges multiple iterators into one.
type stringSortedMergeIterator struct {
inputs bufStringIterators
opt IteratorOptions
heap stringHeap
}
// Close closes the underlying iterators.
func (itr *stringSortedMergeIterator) Close() error { return itr.inputs.Close() }
// Next returns the next points from the iterator.
func (itr *stringSortedMergeIterator) Next() *StringPoint { return itr.pop() }
// pop returns the next point from the heap.
// Reads the next point from item's cursor and puts it back on the heap.
func (itr *stringSortedMergeIterator) pop() *StringPoint {
if len(itr.heap) == 0 {
return nil
}
// Read the next item from the heap.
item := heap.Pop(&itr.heap).(*stringHeapItem)
// Copy the point for return.
p := item.point.Clone()
// Read the next item from the cursor. Push back to heap if one exists.
if item.point = item.itr.Next(); item.point != nil {
heap.Push(&itr.heap, item)
}
return p
}
// stringHeap represents a heap of stringHeapItems.
type stringHeap []*stringHeapItem
func (h stringHeap) Len() int { return len(h) }
func (h stringHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h stringHeap) Less(i, j int) bool {
x, y := h[i].point, h[j].point
if h[i].ascending {
if x.Name != y.Name {
return x.Name < y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() < y.Tags.ID()
}
return x.Time < y.Time
}
if x.Name != y.Name {
return x.Name > y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() > y.Tags.ID()
}
return x.Time > y.Time
}
func (h *stringHeap) Push(x interface{}) {
*h = append(*h, x.(*stringHeapItem))
}
func (h *stringHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
type stringHeapItem struct {
point *StringPoint
itr StringIterator
ascending bool
}
// stringJoinIterator represents a join iterator that processes string values.
type stringJoinIterator struct {
input StringIterator
@ -1199,6 +1402,107 @@ func (itr *booleanMergeIterator) Next() *BooleanPoint {
return nil
}
// newBooleanSortedMergeIterator returns an instance of booleanSortedMergeIterator.
func newBooleanSortedMergeIterator(inputs []BooleanIterator, opt IteratorOptions) Iterator {
itr := &booleanSortedMergeIterator{
inputs: newBufBooleanIterators(inputs),
heap: make(booleanHeap, 0, len(inputs)),
opt: opt,
}
// Initialize heap.
for _, input := range inputs {
// Read next point.
p := input.Next()
if p == nil {
continue
}
// Append to the heap.
itr.heap = append(itr.heap, &booleanHeapItem{point: p, itr: input, ascending: opt.Ascending})
}
heap.Init(&itr.heap)
return itr
}
// booleanSortedMergeIterator is an iterator that sorts and merges multiple iterators into one.
type booleanSortedMergeIterator struct {
inputs bufBooleanIterators
opt IteratorOptions
heap booleanHeap
}
// Close closes the underlying iterators.
func (itr *booleanSortedMergeIterator) Close() error { return itr.inputs.Close() }
// Next returns the next points from the iterator.
func (itr *booleanSortedMergeIterator) Next() *BooleanPoint { return itr.pop() }
// pop returns the next point from the heap.
// Reads the next point from item's cursor and puts it back on the heap.
func (itr *booleanSortedMergeIterator) pop() *BooleanPoint {
if len(itr.heap) == 0 {
return nil
}
// Read the next item from the heap.
item := heap.Pop(&itr.heap).(*booleanHeapItem)
// Copy the point for return.
p := item.point.Clone()
// Read the next item from the cursor. Push back to heap if one exists.
if item.point = item.itr.Next(); item.point != nil {
heap.Push(&itr.heap, item)
}
return p
}
// booleanHeap represents a heap of booleanHeapItems.
type booleanHeap []*booleanHeapItem
func (h booleanHeap) Len() int { return len(h) }
func (h booleanHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h booleanHeap) Less(i, j int) bool {
x, y := h[i].point, h[j].point
if h[i].ascending {
if x.Name != y.Name {
return x.Name < y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() < y.Tags.ID()
}
return x.Time < y.Time
}
if x.Name != y.Name {
return x.Name > y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() > y.Tags.ID()
}
return x.Time > y.Time
}
func (h *booleanHeap) Push(x interface{}) {
*h = append(*h, x.(*booleanHeapItem))
}
func (h *booleanHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
type booleanHeapItem struct {
point *BooleanPoint
itr BooleanIterator
ascending bool
}
// booleanJoinIterator represents a join iterator that processes boolean values.
type booleanJoinIterator struct {
input BooleanIterator

View File

@ -2,6 +2,7 @@ package influxql
import (
"math"
"container/heap"
"sort"
"sync"
)
@ -213,6 +214,107 @@ func (itr *{{.name}}MergeIterator) Next() *{{.Name}}Point {
return nil
}
// new{{.Name}}SortedMergeIterator returns an instance of {{.name}}SortedMergeIterator.
func new{{.Name}}SortedMergeIterator(inputs []{{.Name}}Iterator, opt IteratorOptions) Iterator {
itr := &{{.name}}SortedMergeIterator{
inputs: newBuf{{.Name}}Iterators(inputs),
heap: make({{.name}}Heap, 0, len(inputs)),
opt: opt,
}
// Initialize heap.
for _, input := range inputs {
// Read next point.
p := input.Next()
if p == nil {
continue
}
// Append to the heap.
itr.heap = append(itr.heap, &{{.name}}HeapItem{point: p, itr: input, ascending: opt.Ascending})
}
heap.Init(&itr.heap)
return itr
}
// {{.name}}SortedMergeIterator is an iterator that sorts and merges multiple iterators into one.
type {{.name}}SortedMergeIterator struct {
inputs buf{{.Name}}Iterators
opt IteratorOptions
heap {{.name}}Heap
}
// Close closes the underlying iterators.
func (itr *{{.name}}SortedMergeIterator) Close() error { return itr.inputs.Close() }
// Next returns the next points from the iterator.
func (itr *{{.name}}SortedMergeIterator) Next() *{{.Name}}Point { return itr.pop() }
// pop returns the next point from the heap.
// Reads the next point from item's cursor and puts it back on the heap.
func (itr *{{.name}}SortedMergeIterator) pop() *{{.Name}}Point {
if len(itr.heap) == 0 {
return nil
}
// Read the next item from the heap.
item := heap.Pop(&itr.heap).(*{{.name}}HeapItem)
// Copy the point for return.
p := item.point.Clone()
// Read the next item from the cursor. Push back to heap if one exists.
if item.point = item.itr.Next(); item.point != nil {
heap.Push(&itr.heap, item)
}
return p
}
// {{.name}}Heap represents a heap of {{.name}}HeapItems.
type {{.name}}Heap []*{{.name}}HeapItem
func (h {{.name}}Heap) Len() int { return len(h) }
func (h {{.name}}Heap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h {{.name}}Heap) Less(i, j int) bool {
x, y := h[i].point, h[j].point
if h[i].ascending {
if x.Name != y.Name {
return x.Name < y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() < y.Tags.ID()
}
return x.Time < y.Time
}
if x.Name != y.Name {
return x.Name > y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() > y.Tags.ID()
}
return x.Time > y.Time
}
func (h *{{.name}}Heap) Push(x interface{}) {
*h = append(*h, x.(*{{.name}}HeapItem))
}
func (h *{{.name}}Heap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
type {{.name}}HeapItem struct {
point *{{.Name}}Point
itr {{.Name}}Iterator
ascending bool
}
// {{.name}}JoinIterator represents a join iterator that processes {{.name}} values.
type {{.name}}JoinIterator struct {
input {{.Name}}Iterator

View File

@ -1,7 +1,6 @@
package influxql
import (
"container/heap"
"errors"
"fmt"
"math"
@ -77,105 +76,69 @@ func NewMergeIterator(inputs []Iterator, opt IteratorOptions) Iterator {
}
}
// newFloatSortedMergeIterator returns an instance of floatSortedMergeIterator.
func newFloatSortedMergeIterator(inputs []FloatIterator, opt IteratorOptions) Iterator {
itr := &floatSortedMergeIterator{
inputs: newBufFloatIterators(inputs),
heap: make(floatHeap, 0, len(inputs)),
opt: opt,
// NewLimitIterator returns an iterator that limits the number of points per grouping.
func NewLimitIterator(input Iterator, opt IteratorOptions) Iterator {
switch input := input.(type) {
case FloatIterator:
return newFloatLimitIterator(input, opt)
default:
panic(fmt.Sprintf("unsupported limit iterator type: %T", input))
}
// Initialize heap.
for _, input := range inputs {
// Read next point.
p := input.Next()
if p == nil {
continue
}
// Append to the heap.
itr.heap = append(itr.heap, &floatHeapItem{point: p, itr: input, ascending: opt.Ascending})
}
heap.Init(&itr.heap)
return itr
}
// floatSortedMergeIterator is an iterator that sorts and merges multiple iterators into one.
type floatSortedMergeIterator struct {
inputs bufFloatIterators
opt IteratorOptions
heap floatHeap
// floatLimitIterator represents an iterator that limits points per group.
type floatLimitIterator struct {
input FloatIterator
opt IteratorOptions
n int
prev struct {
name string
tags Tags
}
}
// newFloatLimitIterator returns a new instance of floatLimitIterator.
func newFloatLimitIterator(input FloatIterator, opt IteratorOptions) *floatLimitIterator {
return &floatLimitIterator{
input: input,
opt: opt,
}
}
// Close closes the underlying iterators.
func (itr *floatSortedMergeIterator) Close() error { return itr.inputs.Close() }
func (itr *floatLimitIterator) Close() error { return itr.input.Close() }
// Next returns the next points from the iterator.
func (itr *floatSortedMergeIterator) Next() *FloatPoint { return itr.pop() }
// pop returns the next point from the heap.
// Reads the next point from item's cursor and puts it back on the heap.
func (itr *floatSortedMergeIterator) pop() *FloatPoint {
if len(itr.heap) == 0 {
return nil
}
// Read the next item from the heap.
item := heap.Pop(&itr.heap).(*floatHeapItem)
// Copy the point for return.
p := item.point.Clone()
// Read the next item from the cursor. Push back to heap if one exists.
if item.point = item.itr.Next(); item.point != nil {
heap.Push(&itr.heap, item)
}
return p
}
// floatHeap represents a heap of floatHeapItems.
type floatHeap []*floatHeapItem
func (h floatHeap) Len() int { return len(h) }
func (h floatHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h floatHeap) Less(i, j int) bool {
x, y := h[i].point, h[j].point
if h[i].ascending {
if x.Name != y.Name {
return x.Name < y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() < y.Tags.ID()
// Next returns the next point from the iterator.
func (itr *floatLimitIterator) Next() *FloatPoint {
for {
p := itr.input.Next()
if p == nil {
return nil
}
return x.Time < y.Time
// Reset window and counter if a new window is encountered.
if p.Name != itr.prev.name || !p.Tags.Equals(&itr.prev.tags) {
itr.prev.name = p.Name
itr.prev.tags = p.Tags
itr.n = 0
}
// Increment counter.
itr.n++
// Read next point if not beyond the offset.
if itr.n <= itr.opt.Offset {
continue
}
// Read next point if we're beyond the limit.
if itr.opt.Limit > 0 && (itr.n-itr.opt.Offset) > itr.opt.Limit {
continue
}
return p
}
if x.Name != y.Name {
return x.Name > y.Name
} else if !x.Tags.Equals(&y.Tags) {
return x.Tags.ID() > y.Tags.ID()
}
return x.Time > y.Time
}
func (h *floatHeap) Push(x interface{}) {
*h = append(*h, x.(*floatHeapItem))
}
func (h *floatHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
type floatHeapItem struct {
point *FloatPoint
itr FloatIterator
ascending bool
}
// Join combines inputs based on timestamp and returns new iterators.

View File

@ -78,6 +78,31 @@ func TestFloatAuxIterator(t *testing.T) {
}
}
// Ensure limit iterator returns a subset of points.
func TestLimitIterator(t *testing.T) {
itr := influxql.NewLimitIterator(
&FloatIterator{Points: []influxql.FloatPoint{
{Time: 0, Value: 0},
{Time: 1, Value: 1},
{Time: 2, Value: 2},
{Time: 3, Value: 3},
}},
influxql.IteratorOptions{
Limit: 2,
Offset: 1,
StartTime: influxql.MinTime,
EndTime: influxql.MaxTime,
},
)
if a := (Iterators{itr}).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.FloatPoint{Time: 1, Value: 1}},
{&influxql.FloatPoint{Time: 2, Value: 2}},
}) {
t.Fatalf("unexpected points: %s", spew.Sdump(a))
}
}
func BenchmarkJoin(b *testing.B) {
// Generate inputs.
rand := rand.New(rand.NewSource(0))

145
influxql/point.gen.go Normal file
View File

@ -0,0 +1,145 @@
// Generated by tmpl
// https://github.com/benbjohnson/tmpl
package influxql
// FloatPoint represents a point with a float64 value.
type FloatPoint struct {
Name string
Tags Tags
Time int64
Value float64
Aux []interface{}
}
func (v *FloatPoint) name() string { return v.Name }
func (v *FloatPoint) tags() Tags { return v.Tags }
func (v *FloatPoint) time() int64 { return v.Time }
func (v *FloatPoint) value() interface{} { return v.Value }
func (v *FloatPoint) aux() []interface{} { return v.Aux }
// Clone returns a copy of v.
func (v *FloatPoint) Clone() *FloatPoint {
if v == nil {
return nil
}
other := *v
if v.Aux != nil {
other.Aux = make([]interface{}, len(v.Aux))
copy(other.Aux, v.Aux)
}
return &other
}
// floatPoints represents a slice of points sortable by value.
type floatPoints []FloatPoint
func (a floatPoints) Len() int { return len(a) }
func (a floatPoints) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a floatPoints) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// floatPointsByValue represents a slice of points sortable by value.
type floatPointsByValue []FloatPoint
func (a floatPointsByValue) Len() int { return len(a) }
func (a floatPointsByValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a floatPointsByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// StringPoint represents a point with a string value.
type StringPoint struct {
Name string
Tags Tags
Time int64
Value string
Aux []interface{}
}
func (v *StringPoint) name() string { return v.Name }
func (v *StringPoint) tags() Tags { return v.Tags }
func (v *StringPoint) time() int64 { return v.Time }
func (v *StringPoint) value() interface{} { return v.Value }
func (v *StringPoint) aux() []interface{} { return v.Aux }
// Clone returns a copy of v.
func (v *StringPoint) Clone() *StringPoint {
if v == nil {
return nil
}
other := *v
if v.Aux != nil {
other.Aux = make([]interface{}, len(v.Aux))
copy(other.Aux, v.Aux)
}
return &other
}
// stringPoints represents a slice of points sortable by value.
type stringPoints []StringPoint
func (a stringPoints) Len() int { return len(a) }
func (a stringPoints) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a stringPoints) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// stringPointsByValue represents a slice of points sortable by value.
type stringPointsByValue []StringPoint
func (a stringPointsByValue) Len() int { return len(a) }
func (a stringPointsByValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a stringPointsByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// BooleanPoint represents a point with a bool value.
type BooleanPoint struct {
Name string
Tags Tags
Time int64
Value bool
Aux []interface{}
}
func (v *BooleanPoint) name() string { return v.Name }
func (v *BooleanPoint) tags() Tags { return v.Tags }
func (v *BooleanPoint) time() int64 { return v.Time }
func (v *BooleanPoint) value() interface{} { return v.Value }
func (v *BooleanPoint) aux() []interface{} { return v.Aux }
// Clone returns a copy of v.
func (v *BooleanPoint) Clone() *BooleanPoint {
if v == nil {
return nil
}
other := *v
if v.Aux != nil {
other.Aux = make([]interface{}, len(v.Aux))
copy(other.Aux, v.Aux)
}
return &other
}
// booleanPoints represents a slice of points sortable by value.
type booleanPoints []BooleanPoint
func (a booleanPoints) Len() int { return len(a) }
func (a booleanPoints) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a booleanPoints) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// booleanPointsByValue represents a slice of points sortable by value.
type booleanPointsByValue []BooleanPoint
func (a booleanPointsByValue) Len() int { return len(a) }
func (a booleanPointsByValue) Less(i, j int) bool { return !a[i].Value }
func (a booleanPointsByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@ -0,0 +1,54 @@
package influxql
{{range .}}
// {{.Name}}Point represents a point with a {{.Type}} value.
type {{.Name}}Point struct {
Name string
Tags Tags
Time int64
Value {{.Type}}
Aux []interface{}
}
func (v *{{.Name}}Point) name() string { return v.Name }
func (v *{{.Name}}Point) tags() Tags { return v.Tags }
func (v *{{.Name}}Point) time() int64 { return v.Time }
func (v *{{.Name}}Point) value() interface{} { return v.Value }
func (v *{{.Name}}Point) aux() []interface{} { return v.Aux }
// Clone returns a copy of v.
func (v *{{.Name}}Point) Clone() *{{.Name}}Point {
if v == nil {
return nil
}
other := *v
if v.Aux != nil {
other.Aux = make([]interface{}, len(v.Aux))
copy(other.Aux, v.Aux)
}
return &other
}
// {{.name}}Points represents a slice of points sortable by value.
type {{.name}}Points []{{.Name}}Point
func (a {{.name}}Points) Len() int { return len(a) }
func (a {{.name}}Points) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a {{.name}}Points) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// {{.name}}PointsByValue represents a slice of points sortable by value.
type {{.name}}PointsByValue []{{.Name}}Point
func (a {{.name}}PointsByValue) Len() int { return len(a) }
{{if eq .Name "Boolean"}}
func (a {{.name}}PointsByValue) Less(i, j int) bool { return !a[i].Value }
{{else}}
func (a {{.name}}PointsByValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
{{end}}
func (a {{.name}}PointsByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
{{end}}

View File

@ -4,6 +4,8 @@ import (
"sort"
)
//go:generate tmpl -data=[{"Name":"Float","name":"float","Type":"float64","Nil":"math.NaN()"},{"Name":"String","name":"string","Type":"string","Nil":"\"\""},{"Name":"Boolean","name":"boolean","Type":"bool","Nil":"false"}] point.gen.go.tmpl
// ZeroTime is the Unix nanosecond timestamp for time.Time{}.
const ZeroTime = int64(-6795364578871345152)
@ -26,90 +28,6 @@ type Point interface {
// Points represents a list of points.
type Points []Point
// FloatPoint represents a point with a float value.
type FloatPoint struct {
Name string
Tags Tags
Time int64
Value float64
Aux []interface{}
}
func (v *FloatPoint) name() string { return v.Name }
func (v *FloatPoint) tags() Tags { return v.Tags }
func (v *FloatPoint) time() int64 { return v.Time }
func (v *FloatPoint) value() interface{} { return v.Value }
func (v *FloatPoint) aux() []interface{} { return v.Aux }
// Clone returns a copy of v.
func (v *FloatPoint) Clone() *FloatPoint {
if v == nil {
return nil
}
other := *v
if v.Aux != nil {
other.Aux = make([]interface{}, len(v.Aux))
copy(other.Aux, v.Aux)
}
return &other
}
// floatPoints represents a slice of points sortable by value.
type floatPoints []FloatPoint
func (a floatPoints) Len() int { return len(a) }
func (a floatPoints) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a floatPoints) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// floatPointsByValue represents a slice of points sortable by value.
type floatPointsByValue []FloatPoint
func (a floatPointsByValue) Len() int { return len(a) }
func (a floatPointsByValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a floatPointsByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// StringPoint represents a point with a string value.
type StringPoint struct {
Name string
Tags Tags
Time int64
Value string
Aux []interface{}
}
func (v *StringPoint) name() string { return v.Name }
func (v *StringPoint) tags() Tags { return v.Tags }
func (v *StringPoint) time() int64 { return v.Time }
func (v *StringPoint) value() interface{} { return v.Value }
func (v *StringPoint) aux() []interface{} { return v.Aux }
// stringPoints represents a slice of points sortable by value.
type stringPoints []StringPoint
func (a stringPoints) Len() int { return len(a) }
func (a stringPoints) Less(i, j int) bool { return a[i].Time < a[j].Time }
func (a stringPoints) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// BooleanPoint represents a point with a boolean value.
type BooleanPoint struct {
Name string
Tags Tags
Time int64
Value bool
Aux []interface{}
}
func (v *BooleanPoint) name() string { return v.Name }
func (v *BooleanPoint) tags() Tags { return v.Tags }
func (v *BooleanPoint) time() int64 { return v.Time }
func (v *BooleanPoint) value() interface{} { return v.Value }
func (v *BooleanPoint) aux() []interface{} { return v.Aux }
// Tags represent a map of keys and values.
// It memoizes its key so it can be used efficiently during query execution.
type Tags struct {

View File

@ -46,6 +46,11 @@ func buildAuxIterators(fields Fields, ic IteratorCreator, opt IteratorOptions) (
return nil, err
}
// Apply limit & offset.
if opt.Limit > 0 || opt.Offset > 0 {
input = NewLimitIterator(input, opt)
}
// Wrap in an auxilary iterator to separate the fields.
aitr := NewAuxIterator(input, opt)
@ -91,6 +96,13 @@ func buildExprIterators(fields Fields, ic IteratorCreator, opt IteratorOptions)
// Join iterators together on time.
itrs = Join(itrs)
// If there is a limit or offset then apply it.
if opt.Limit > 0 || opt.Offset > 0 {
for i := range itrs {
itrs[i] = NewLimitIterator(itrs[i], opt)
}
}
return itrs, nil
}