LIMIT/OFFSET
parent
036382ee20
commit
60b051ee88
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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] }
|
|
@ -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}}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue