influxdb/query/functions/sample.go

179 lines
3.9 KiB
Go

package functions
import (
"fmt"
"math/rand"
"github.com/influxdata/platform/query"
"github.com/influxdata/platform/query/execute"
"github.com/influxdata/platform/query/plan"
"github.com/influxdata/platform/query/semantic"
)
const SampleKind = "sample"
type SampleOpSpec struct {
N int64 `json:"n"`
Pos int64 `json:"pos"`
execute.SelectorConfig
}
var sampleSignature = query.DefaultFunctionSignature()
func init() {
sampleSignature.Params["n"] = semantic.Int
sampleSignature.Params["pos"] = semantic.Int
query.RegisterFunction(SampleKind, createSampleOpSpec, sampleSignature)
query.RegisterOpSpec(SampleKind, newSampleOp)
plan.RegisterProcedureSpec(SampleKind, newSampleProcedure, SampleKind)
execute.RegisterTransformation(SampleKind, createSampleTransformation)
}
func createSampleOpSpec(args query.Arguments, a *query.Administration) (query.OperationSpec, error) {
if err := a.AddParentFromArgs(args); err != nil {
return nil, err
}
spec := new(SampleOpSpec)
n, err := args.GetRequiredInt("n")
if err != nil {
return nil, err
}
spec.N = n
if pos, ok, err := args.GetInt("pos"); err != nil {
return nil, err
} else if ok {
spec.Pos = pos
} else {
spec.Pos = -1
}
if err := spec.SelectorConfig.ReadArgs(args); err != nil {
return nil, err
}
return spec, nil
}
func newSampleOp() query.OperationSpec {
return new(SampleOpSpec)
}
func (s *SampleOpSpec) Kind() query.OperationKind {
return SampleKind
}
type SampleProcedureSpec struct {
N int64
Pos int64
execute.SelectorConfig
}
func newSampleProcedure(qs query.OperationSpec, pa plan.Administration) (plan.ProcedureSpec, error) {
spec, ok := qs.(*SampleOpSpec)
if !ok {
return nil, fmt.Errorf("invalid spec type %T", qs)
}
return &SampleProcedureSpec{
N: spec.N,
Pos: spec.Pos,
SelectorConfig: spec.SelectorConfig,
}, nil
}
func (s *SampleProcedureSpec) Kind() plan.ProcedureKind {
return SampleKind
}
func (s *SampleProcedureSpec) Copy() plan.ProcedureSpec {
ns := new(SampleProcedureSpec)
ns.N = s.N
ns.Pos = s.Pos
ns.SelectorConfig = s.SelectorConfig
return ns
}
type SampleSelector struct {
N int
Pos int
offset int
selected []int
}
func createSampleTransformation(id execute.DatasetID, mode execute.AccumulationMode, spec plan.ProcedureSpec, a execute.Administration) (execute.Transformation, execute.Dataset, error) {
ps, ok := spec.(*SampleProcedureSpec)
if !ok {
return nil, nil, fmt.Errorf("invalid spec type %T", ps)
}
ss := &SampleSelector{
N: int(ps.N),
Pos: int(ps.Pos),
}
t, d := execute.NewIndexSelectorTransformationAndDataset(id, mode, ss, ps.SelectorConfig, a.Allocator())
return t, d, nil
}
func (s *SampleSelector) reset() {
pos := s.Pos
if pos < 0 {
pos = rand.Intn(s.N)
}
s.offset = pos
}
func (s *SampleSelector) NewBoolSelector() execute.DoBoolIndexSelector {
s.reset()
return s
}
func (s *SampleSelector) NewIntSelector() execute.DoIntIndexSelector {
s.reset()
return s
}
func (s *SampleSelector) NewUIntSelector() execute.DoUIntIndexSelector {
s.reset()
return s
}
func (s *SampleSelector) NewFloatSelector() execute.DoFloatIndexSelector {
s.reset()
return s
}
func (s *SampleSelector) NewStringSelector() execute.DoStringIndexSelector {
s.reset()
return s
}
func (s *SampleSelector) selectSample(l int) []int {
var i int
s.selected = s.selected[0:0]
for i = s.offset; i < l; i += s.N {
s.selected = append(s.selected, i)
}
s.offset = i - l
return s.selected
}
func (s *SampleSelector) DoBool(vs []bool) []int {
return s.selectSample(len(vs))
}
func (s *SampleSelector) DoInt(vs []int64) []int {
return s.selectSample(len(vs))
}
func (s *SampleSelector) DoUInt(vs []uint64) []int {
return s.selectSample(len(vs))
}
func (s *SampleSelector) DoFloat(vs []float64) []int {
return s.selectSample(len(vs))
}
func (s *SampleSelector) DoString(vs []string) []int {
return s.selectSample(len(vs))
}