mirror of
https://gitee.com/rulego/streamsql.git
synced 2025-07-06 16:08:36 +00:00
init
This commit is contained in:
48
.gitignore
vendored
Normal file
48
.gitignore
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# JetBrains template
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
out/
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
### Go template
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
### macOS template
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# log
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# coverage file
|
||||||
|
coverage.html
|
||||||
|
|
||||||
|
examples/server/rules/
|
||||||
|
examples/server/js/
|
||||||
|
examples/server/plugins/
|
111
builtin/builtin.go
Normal file
111
builtin/builtin.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/montanaflynn/stats"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AggregateBuiltins = map[string]*AggregateFunction{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for _, item := range aggregateBuiltins {
|
||||||
|
AggregateBuiltins[item.Name] = item
|
||||||
|
for _, name := range item.Alias {
|
||||||
|
AggregateBuiltins[name] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var aggregateBuiltins = []*AggregateFunction{
|
||||||
|
{
|
||||||
|
Name: "avg",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Mean(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "count",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return len(input), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "sum",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Sum(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "min",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Min(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "max",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Max(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//返回组中所有值的标准差。空值不参与计算。
|
||||||
|
{
|
||||||
|
Name: "stddev",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.StandardDeviation(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//返回组中所有值的样本标准差。空值不参与计算。
|
||||||
|
{
|
||||||
|
Name: "stddevs",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.StandardDeviationSample(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "var",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Variance(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "vars",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.VarS(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "median",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
return stats.Median(input)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "percentile",
|
||||||
|
Func: func(input []float64, args ...any) (any, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return 0, fmt.Errorf("invalid number of arguments for percentile (expected 1, got %d)", len(args))
|
||||||
|
}
|
||||||
|
if percent, ok := args[0].(float64); !ok {
|
||||||
|
return 0, fmt.Errorf("percent need float64 type (got %d)", len(args))
|
||||||
|
} else {
|
||||||
|
return stats.Percentile(input, percent)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
38
builtin/function.go
Normal file
38
builtin/function.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
//type Function struct {
|
||||||
|
// AuthorName string
|
||||||
|
// Func func(args ...any) (any, error)
|
||||||
|
// Fast func(arg any) any
|
||||||
|
// ValidateArgs func(args ...any) (any, error)
|
||||||
|
// Types []reflect.Type
|
||||||
|
// Validate func(args []reflect.Type) (reflect.Type, error)
|
||||||
|
// Predicate bool
|
||||||
|
//}
|
||||||
|
|
||||||
|
type AggregateFunction struct {
|
||||||
|
//函数名
|
||||||
|
Name string
|
||||||
|
//别名
|
||||||
|
Alias []string
|
||||||
|
//函数
|
||||||
|
Func func(input []float64, args ...any) (any, error)
|
||||||
|
//验证参数
|
||||||
|
ValidateArgs func(input []float64, args ...any) (any, error)
|
||||||
|
}
|
26
collector/aggregate_collector.go
Normal file
26
collector/aggregate_collector.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aggregateCollector struct {
|
||||||
|
groupBy []string
|
||||||
|
window types.Window
|
||||||
|
}
|
263
collector/collector.go
Normal file
263
collector/collector.go
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/rulego/streamsql/dataset"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultSelectStreamSqlContext sql执行生命周期上下文
|
||||||
|
type DefaultSelectStreamSqlContext struct {
|
||||||
|
context.Context
|
||||||
|
Collector *StreamCollector
|
||||||
|
// 当前传入已经转成map的流数据
|
||||||
|
msgAsMap map[string]interface{}
|
||||||
|
msg types.Msg
|
||||||
|
locker sync.RWMutex
|
||||||
|
groupValues types.GroupValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultSelectStreamSqlContext(ctx context.Context, collector *StreamCollector, msg types.Msg) *DefaultSelectStreamSqlContext {
|
||||||
|
return &DefaultSelectStreamSqlContext{
|
||||||
|
Context: ctx,
|
||||||
|
Collector: collector,
|
||||||
|
msg: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (c *DefaultSelectStreamSqlContext) Decode() error {
|
||||||
|
if c.msg.Data == nil {
|
||||||
|
return errors.New("data can not nil")
|
||||||
|
}
|
||||||
|
//已经解码
|
||||||
|
if c.msgAsMap != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if mapData, err := c.msg.Data.DecodeAsMap(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
c.msgAsMap = mapData
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) InputAsMap() map[string]interface{} {
|
||||||
|
c.locker.RLock()
|
||||||
|
defer c.locker.RUnlock()
|
||||||
|
return c.msgAsMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) RawInput() types.Msg {
|
||||||
|
return c.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) SetGroupByKey(groupByKey types.GroupFields) {
|
||||||
|
c.Collector.SetGroupByKey(groupByKey)
|
||||||
|
}
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetGroupByKey() types.GroupFields {
|
||||||
|
return c.Collector.GetGroupByKey()
|
||||||
|
}
|
||||||
|
func (c *DefaultSelectStreamSqlContext) IsInitWindow(groupValues types.GroupValues) bool {
|
||||||
|
return c.Collector.IsInitWindow(groupValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) AddWindow(groupByKey types.GroupValues, window types.Window) {
|
||||||
|
c.Collector.AddWindow(groupByKey, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) CreteWindowObserver() types.WindowObserver {
|
||||||
|
return c.Collector.CreteWindowObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetWindow(groupByKey types.GroupValues) types.Window {
|
||||||
|
return c.Collector.GetWindow(groupByKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetRow(groupValues types.GroupValues) (*dataset.Row, bool) {
|
||||||
|
return c.Collector.GetRow(groupValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) AddColumn(groupValues types.GroupValues, kv dataset.KeyValue) {
|
||||||
|
c.Collector.AddColumn(groupValues, kv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetColumn(groupValues types.GroupValues, key dataset.Key) (dataset.KeyValue, bool) {
|
||||||
|
return c.Collector.GetColumn(groupValues, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (c *DefaultSelectStreamSqlContext) AddField(fieldId string, value any) {
|
||||||
|
// c.locker.Lock()
|
||||||
|
// defer c.locker.Unlock()
|
||||||
|
// c.msgAsMap[fieldId] = value
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (c *DefaultSelectStreamSqlContext) GetField(fieldId string) (any, bool) {
|
||||||
|
// c.locker.RLock()
|
||||||
|
// defer c.locker.RUnlock()
|
||||||
|
// v, ok := c.msgAsMap[fieldId]
|
||||||
|
// return v, ok
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) SetCurrentGroupValues(groupValues types.GroupValues) {
|
||||||
|
c.groupValues = groupValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetCurrentGroupValues() types.GroupValues {
|
||||||
|
return c.groupValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) AddFieldAggregateValue(groupValues types.GroupValues, fieldId string, value float64) {
|
||||||
|
c.Collector.AddFieldAggregateValue(groupValues, fieldId, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DefaultSelectStreamSqlContext) GetFieldAggregateValue(groupValues types.GroupValues, fieldId string) []float64 {
|
||||||
|
return c.Collector.GetFieldAggregateValue(groupValues, fieldId)
|
||||||
|
}
|
||||||
|
|
||||||
|
type aggregateFieldValue struct {
|
||||||
|
GroupFields types.GroupFields
|
||||||
|
FieldWindows map[string]types.Window
|
||||||
|
}
|
||||||
|
|
||||||
|
func (afv *aggregateFieldValue) AddFieldValue(fieldId string, data float64) {
|
||||||
|
if w, ok := afv.FieldWindows[fieldId]; !ok {
|
||||||
|
//afv.FieldWindows[fieldId] = newQueue
|
||||||
|
} else {
|
||||||
|
w.Add(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (afv *aggregateFieldValue) GetFieldValues(fieldId string) []float64 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamCollector 收集器
|
||||||
|
type StreamCollector struct {
|
||||||
|
context.Context
|
||||||
|
keyedWindow map[types.GroupValues]types.Window
|
||||||
|
planner types.LogicalPlan
|
||||||
|
aggregateOperators []types.AggregateOperator
|
||||||
|
groupByKey types.GroupFields
|
||||||
|
aggregateFieldValues map[types.GroupValues]*aggregateFieldValue
|
||||||
|
rows *dataset.Rows
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) SetGroupByKey(groupByKey types.GroupFields) {
|
||||||
|
c.groupByKey = groupByKey
|
||||||
|
}
|
||||||
|
func (c *StreamCollector) GetGroupByKey() types.GroupFields {
|
||||||
|
return c.groupByKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) IsInitWindow(groupValues types.GroupValues) bool {
|
||||||
|
_, ok := c.keyedWindow[groupValues]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) AddWindow(groupByKey types.GroupValues, window types.Window) {
|
||||||
|
c.keyedWindow[groupByKey] = window
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) GetWindow(groupByKey types.GroupValues) types.Window {
|
||||||
|
return c.keyedWindow[groupByKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) CreteWindowObserver() types.WindowObserver {
|
||||||
|
return types.WindowObserver{
|
||||||
|
AddHandler: func(context types.StreamSqlOperatorContext, data float64) {
|
||||||
|
for _, op := range c.aggregateOperators {
|
||||||
|
op.AddHandler(context, data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ArchiveHandler: func(context types.StreamSqlOperatorContext, dataList []float64) {
|
||||||
|
for _, op := range c.aggregateOperators {
|
||||||
|
op.ArchiveHandler(context, dataList)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StartHandler: func(context types.StreamSqlOperatorContext) {
|
||||||
|
},
|
||||||
|
EndHandler: func(context types.StreamSqlOperatorContext, dataList []float64) {
|
||||||
|
for _, op := range c.aggregateOperators {
|
||||||
|
op.EndHandler(context, dataList)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (c *StreamCollector) AddFieldAggregateValue(groupValues types.GroupValues, fieldId string, data float64) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
if w, ok := c.keyedWindow[groupValues]; ok {
|
||||||
|
w.Add(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) GetFieldAggregateValue(groupValues types.GroupValues, fieldId string) []float64 {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
if _, ok := c.keyedWindow[groupValues]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) GetRow(groupValues types.GroupValues) (*dataset.Row, bool) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
return c.rows.GetRow(groupValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) AddColumn(groupValues types.GroupValues, kv dataset.KeyValue) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
c.rows.AddColumn(groupValues, kv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) GetColumn(groupValues types.GroupValues, key dataset.Key) (dataset.KeyValue, bool) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
return c.rows.GetColumn(groupValues, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamCollector(planner types.LogicalPlan) *StreamCollector {
|
||||||
|
c := &StreamCollector{
|
||||||
|
planner: planner,
|
||||||
|
aggregateOperators: planner.AggregateOperators(),
|
||||||
|
keyedWindow: make(map[types.GroupValues]types.Window),
|
||||||
|
aggregateFieldValues: make(map[types.GroupValues]*aggregateFieldValue),
|
||||||
|
rows: dataset.NewRows(),
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init 初始化
|
||||||
|
func (c *StreamCollector) Init() error {
|
||||||
|
return c.planner.Init(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StreamCollector) Collect(ctx context.Context, msg types.Msg) error {
|
||||||
|
selectStreamSqlContext := NewDefaultSelectStreamSqlContext(ctx, c, msg)
|
||||||
|
if err := selectStreamSqlContext.Decode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.planner.Apply(selectStreamSqlContext)
|
||||||
|
}
|
41
collector/collector_test.go
Normal file
41
collector/collector_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCollector(t *testing.T) {
|
||||||
|
sql := "select deviceId,avg(temperature+20/20,aa)/2 as aa from Input where deviceId='aa' OR deviceId='bb' group by deviceId,TumblingWindow(10m) ;"
|
||||||
|
newParser := rsql.NewParser(rsql.NewLexer(sql))
|
||||||
|
statement := newParser.ParseStatement()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
statement.Format(buf)
|
||||||
|
buf.WriteTo(os.Stdout)
|
||||||
|
fmt.Println(statement)
|
||||||
|
//collector, err := planner.CreateSelectPlanner(statement.(*rsql.Select))
|
||||||
|
//fmt.Println(err)
|
||||||
|
//msg := types.NewJsonMsg(0, types.NewMetadata(), `{"temperature":50}`)
|
||||||
|
//err = collector.Collect(context.TODO(), msg)
|
||||||
|
//fmt.Println(err)
|
||||||
|
//time.Sleep(time.Second * 10)
|
||||||
|
}
|
17
conf/config.go
Normal file
17
conf/config.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conf
|
134
dataset/key.go
Normal file
134
dataset/key.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package dataset // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
// Key represents the key part in key-value pairs. It's a string. The
|
||||||
|
// allowed character set in the key depends on the use of the key.
|
||||||
|
type Key string
|
||||||
|
|
||||||
|
// Bool creates a KeyValue instance with a BOOL Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Bool(name, value).
|
||||||
|
func (k Key) Bool(v bool) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: BoolValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice creates a KeyValue instance with a BOOLSLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- BoolSlice(name, value).
|
||||||
|
func (k Key) BoolSlice(v []bool) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: BoolSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int creates a KeyValue instance with an INT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int(name, value).
|
||||||
|
func (k Key) Int(v int) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: IntValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice creates a KeyValue instance with an INT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- IntSlice(name, value).
|
||||||
|
func (k Key) IntSlice(v []int) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: IntSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 creates a KeyValue instance with an INT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int64(name, value).
|
||||||
|
func (k Key) Int64(v int64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Int64Value(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice creates a KeyValue instance with an INT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int64Slice(name, value).
|
||||||
|
func (k Key) Int64Slice(v []int64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Int64SliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 creates a KeyValue instance with a FLOAT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Float64(name, value).
|
||||||
|
func (k Key) Float64(v float64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Float64Value(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Float64(name, value).
|
||||||
|
func (k Key) Float64Slice(v []float64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Float64SliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a KeyValue instance with a STRING Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- String(name, value).
|
||||||
|
func (k Key) String(v string) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: StringValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice creates a KeyValue instance with a STRINGSLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- StringSlice(name, value).
|
||||||
|
func (k Key) StringSlice(v []string) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: StringSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defined returns true for non-empty keys.
|
||||||
|
func (k Key) Defined() bool {
|
||||||
|
return len(k) != 0
|
||||||
|
}
|
86
dataset/kv.go
Normal file
86
dataset/kv.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package dataset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyValue holds a key and value pair.
|
||||||
|
type KeyValue struct {
|
||||||
|
Key Key
|
||||||
|
Value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns if kv is a valid OpenTelemetry attribute.
|
||||||
|
func (kv KeyValue) Valid() bool {
|
||||||
|
return kv.Key.Defined() && kv.Value.Type() != INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool creates a KeyValue with a BOOL Value type.
|
||||||
|
func Bool(k string, v bool) KeyValue {
|
||||||
|
return Key(k).Bool(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice creates a KeyValue with a BOOLSLICE Value type.
|
||||||
|
func BoolSlice(k string, v []bool) KeyValue {
|
||||||
|
return Key(k).BoolSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int creates a KeyValue with an INT64 Value type.
|
||||||
|
func Int(k string, v int) KeyValue {
|
||||||
|
return Key(k).Int(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice creates a KeyValue with an INT64SLICE Value type.
|
||||||
|
func IntSlice(k string, v []int) KeyValue {
|
||||||
|
return Key(k).IntSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 creates a KeyValue with an INT64 Value type.
|
||||||
|
func Int64(k string, v int64) KeyValue {
|
||||||
|
return Key(k).Int64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice creates a KeyValue with an INT64SLICE Value type.
|
||||||
|
func Int64Slice(k string, v []int64) KeyValue {
|
||||||
|
return Key(k).Int64Slice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 creates a KeyValue with a FLOAT64 Value type.
|
||||||
|
func Float64(k string, v float64) KeyValue {
|
||||||
|
return Key(k).Float64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type.
|
||||||
|
func Float64Slice(k string, v []float64) KeyValue {
|
||||||
|
return Key(k).Float64Slice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a KeyValue with a STRING Value type.
|
||||||
|
func String(k, v string) KeyValue {
|
||||||
|
return Key(k).String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice creates a KeyValue with a STRINGSLICE Value type.
|
||||||
|
func StringSlice(k string, v []string) KeyValue {
|
||||||
|
return Key(k).StringSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stringer creates a new key-value pair with a passed name and a string
|
||||||
|
// value generated by the passed Stringer interface.
|
||||||
|
func Stringer(k string, v fmt.Stringer) KeyValue {
|
||||||
|
return Key(k).String(v.String())
|
||||||
|
}
|
99
dataset/row.go
Normal file
99
dataset/row.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dataset
|
||||||
|
|
||||||
|
import "github.com/rulego/streamsql/types"
|
||||||
|
|
||||||
|
type Row struct {
|
||||||
|
GroupValues types.GroupValues
|
||||||
|
Columns map[Key]KeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRow(groupValues types.GroupValues) *Row {
|
||||||
|
r := &Row{
|
||||||
|
GroupValues: groupValues,
|
||||||
|
Columns: make(map[Key]KeyValue),
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Row) AddColumn(kv KeyValue) {
|
||||||
|
if r.Columns == nil {
|
||||||
|
r.Columns = make(map[Key]KeyValue)
|
||||||
|
}
|
||||||
|
r.Columns[kv.Key] = kv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Row) GetColumn(key Key) (KeyValue, bool) {
|
||||||
|
if r.Columns == nil {
|
||||||
|
return KeyValue{}, false
|
||||||
|
}
|
||||||
|
v, ok := r.Columns[key]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rows struct {
|
||||||
|
Rows map[types.GroupValues]*Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRows() *Rows {
|
||||||
|
r := &Rows{
|
||||||
|
Rows: make(map[types.GroupValues]*Row),
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rows) AddColumn(groupValues types.GroupValues, kv KeyValue) {
|
||||||
|
if r.Rows == nil {
|
||||||
|
r.Rows = make(map[types.GroupValues]*Row)
|
||||||
|
}
|
||||||
|
row, ok := r.Rows[groupValues]
|
||||||
|
if !ok {
|
||||||
|
row = NewRow(groupValues)
|
||||||
|
r.Rows[groupValues] = row
|
||||||
|
}
|
||||||
|
row.AddColumn(kv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rows) GetColumn(groupValues types.GroupValues, key Key) (KeyValue, bool) {
|
||||||
|
if r.Rows == nil {
|
||||||
|
return KeyValue{}, false
|
||||||
|
}
|
||||||
|
row, ok := r.Rows[groupValues]
|
||||||
|
if !ok {
|
||||||
|
return KeyValue{}, false
|
||||||
|
}
|
||||||
|
return row.GetColumn(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rows) GetRow(groupValues types.GroupValues) (*Row, bool) {
|
||||||
|
if r.Rows == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
v, ok := r.Rows[groupValues]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rows) AddRow(row *Row) {
|
||||||
|
if row == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Rows == nil {
|
||||||
|
r.Rows = make(map[types.GroupValues]*Row)
|
||||||
|
}
|
||||||
|
r.Rows[row.GroupValues] = row
|
||||||
|
}
|
269
dataset/value.go
Normal file
269
dataset/value.go
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package dataset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/rulego/streamsql/utils/attribute"
|
||||||
|
"github.com/rulego/streamsql/utils/compress"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=Type
|
||||||
|
|
||||||
|
// Type describes the type of the data Value holds.
|
||||||
|
type Type int // nolint: revive // redefines builtin Type.
|
||||||
|
|
||||||
|
// Value represents the value part in key-value pairs.
|
||||||
|
type Value struct {
|
||||||
|
vtype Type
|
||||||
|
numeric uint64
|
||||||
|
stringly string
|
||||||
|
slice interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// INVALID is used for a Value with no value set.
|
||||||
|
INVALID Type = iota
|
||||||
|
// BOOL is a boolean Type Value.
|
||||||
|
BOOL
|
||||||
|
// INT64 is a 64-bit signed integral Type Value.
|
||||||
|
INT64
|
||||||
|
// FLOAT64 is a 64-bit floating point Type Value.
|
||||||
|
FLOAT64
|
||||||
|
// STRING is a string Type Value.
|
||||||
|
STRING
|
||||||
|
// BOOLSLICE is a slice of booleans Type Value.
|
||||||
|
BOOLSLICE
|
||||||
|
// INT64SLICE is a slice of 64-bit signed integral numbers Type Value.
|
||||||
|
INT64SLICE
|
||||||
|
// FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value.
|
||||||
|
FLOAT64SLICE
|
||||||
|
// STRINGSLICE is a slice of strings Type Value.
|
||||||
|
STRINGSLICE
|
||||||
|
)
|
||||||
|
|
||||||
|
// BoolValue creates a BOOL Value.
|
||||||
|
func BoolValue(v bool) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: BOOL,
|
||||||
|
numeric: compress.BoolToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSliceValue creates a BOOLSLICE Value.
|
||||||
|
func BoolSliceValue(v []bool) Value {
|
||||||
|
return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValue creates an INT64 Value.
|
||||||
|
func IntValue(v int) Value {
|
||||||
|
return Int64Value(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceValue creates an INTSLICE Value.
|
||||||
|
func IntSliceValue(v []int) Value {
|
||||||
|
var int64Val int64
|
||||||
|
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(int64Val)))
|
||||||
|
for i, val := range v {
|
||||||
|
cp.Elem().Index(i).SetInt(int64(val))
|
||||||
|
}
|
||||||
|
return Value{
|
||||||
|
vtype: INT64SLICE,
|
||||||
|
slice: cp.Elem().Interface(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Value creates an INT64 Value.
|
||||||
|
func Int64Value(v int64) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: INT64,
|
||||||
|
numeric: compress.Int64ToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64SliceValue creates an INT64SLICE Value.
|
||||||
|
func Int64SliceValue(v []int64) Value {
|
||||||
|
return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Value creates a FLOAT64 Value.
|
||||||
|
func Float64Value(v float64) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: FLOAT64,
|
||||||
|
numeric: compress.Float64ToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64SliceValue creates a FLOAT64SLICE Value.
|
||||||
|
func Float64SliceValue(v []float64) Value {
|
||||||
|
return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValue creates a STRING Value.
|
||||||
|
func StringValue(v string) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: STRING,
|
||||||
|
stringly: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceValue creates a STRINGSLICE Value.
|
||||||
|
func StringSliceValue(v []string) Value {
|
||||||
|
return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns a type of the Value.
|
||||||
|
func (v Value) Type() Type {
|
||||||
|
return v.vtype
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsBool returns the bool value. Make sure that the Value's type is
|
||||||
|
// BOOL.
|
||||||
|
func (v Value) AsBool() bool {
|
||||||
|
return compress.RawToBool(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsBoolSlice returns the []bool value. Make sure that the Value's type is
|
||||||
|
// BOOLSLICE.
|
||||||
|
func (v Value) AsBoolSlice() []bool {
|
||||||
|
if v.vtype != BOOLSLICE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.asBoolSlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) asBoolSlice() []bool {
|
||||||
|
return attribute.AsBoolSlice(v.slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64 returns the int64 value. Make sure that the Value's type is
|
||||||
|
// INT64.
|
||||||
|
func (v Value) AsInt64() int64 {
|
||||||
|
return compress.RawToInt64(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64Slice returns the []int64 value. Make sure that the Value's type is
|
||||||
|
// INT64SLICE.
|
||||||
|
func (v Value) AsInt64Slice() []int64 {
|
||||||
|
if v.vtype != INT64SLICE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.asInt64Slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) asInt64Slice() []int64 {
|
||||||
|
return attribute.AsInt64Slice(v.slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64 returns the float64 value. Make sure that the Value's
|
||||||
|
// type is FLOAT64.
|
||||||
|
func (v Value) AsFloat64() float64 {
|
||||||
|
return compress.RawToFloat64(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is
|
||||||
|
// FLOAT64SLICE.
|
||||||
|
func (v Value) AsFloat64Slice() []float64 {
|
||||||
|
if v.vtype != FLOAT64SLICE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.asFloat64Slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) asFloat64Slice() []float64 {
|
||||||
|
return attribute.AsFloat64Slice(v.slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsString returns the string value. Make sure that the Value's type
|
||||||
|
// is STRING.
|
||||||
|
func (v Value) AsString() string {
|
||||||
|
return v.stringly
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsStringSlice returns the []string value. Make sure that the Value's type is
|
||||||
|
// STRINGSLICE.
|
||||||
|
func (v Value) AsStringSlice() []string {
|
||||||
|
if v.vtype != STRINGSLICE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.asStringSlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) asStringSlice() []string {
|
||||||
|
return attribute.AsStringSlice(v.slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
type unknownValueType struct{}
|
||||||
|
|
||||||
|
// AsInterface returns Value's data as interface{}.
|
||||||
|
func (v Value) AsInterface() interface{} {
|
||||||
|
switch v.Type() {
|
||||||
|
case BOOL:
|
||||||
|
return v.AsBool()
|
||||||
|
case BOOLSLICE:
|
||||||
|
return v.asBoolSlice()
|
||||||
|
case INT64:
|
||||||
|
return v.AsInt64()
|
||||||
|
case INT64SLICE:
|
||||||
|
return v.asInt64Slice()
|
||||||
|
case FLOAT64:
|
||||||
|
return v.AsFloat64()
|
||||||
|
case FLOAT64SLICE:
|
||||||
|
return v.asFloat64Slice()
|
||||||
|
case STRING:
|
||||||
|
return v.stringly
|
||||||
|
case STRINGSLICE:
|
||||||
|
return v.asStringSlice()
|
||||||
|
}
|
||||||
|
return unknownValueType{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit returns a string representation of Value's data.
|
||||||
|
func (v Value) Emit() string {
|
||||||
|
switch v.Type() {
|
||||||
|
case BOOLSLICE:
|
||||||
|
return fmt.Sprint(v.asBoolSlice())
|
||||||
|
case BOOL:
|
||||||
|
return strconv.FormatBool(v.AsBool())
|
||||||
|
case INT64SLICE:
|
||||||
|
return fmt.Sprint(v.asInt64Slice())
|
||||||
|
case INT64:
|
||||||
|
return strconv.FormatInt(v.AsInt64(), 10)
|
||||||
|
case FLOAT64SLICE:
|
||||||
|
return fmt.Sprint(v.asFloat64Slice())
|
||||||
|
case FLOAT64:
|
||||||
|
return fmt.Sprint(v.AsFloat64())
|
||||||
|
case STRINGSLICE:
|
||||||
|
return fmt.Sprint(v.asStringSlice())
|
||||||
|
case STRING:
|
||||||
|
return v.stringly
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the JSON encoding of the Value.
|
||||||
|
func (v Value) MarshalJSON() ([]byte, error) {
|
||||||
|
var jsonVal struct {
|
||||||
|
Type string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
jsonVal.Type = v.Type()
|
||||||
|
jsonVal.Value = v.AsInterface()
|
||||||
|
return json.Marshal(jsonVal)
|
||||||
|
}
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module github.com/rulego/streamsql
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/expr-lang/expr v1.16.0
|
||||||
|
github.com/golang/snappy v0.0.4
|
||||||
|
github.com/montanaflynn/stats v0.7.1
|
||||||
|
)
|
10
go.sum
Normal file
10
go.sum
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/expr-lang/expr v1.16.0 h1:BQabx+PbjsL2PEQwkJ4GIn3CcuUh8flduHhJ0lHjWwE=
|
||||||
|
github.com/expr-lang/expr v1.16.0/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
89
operator/aggregate_op.go
Normal file
89
operator/aggregate_op.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/expr-lang/expr/vm"
|
||||||
|
"github.com/rulego/streamsql/builtin"
|
||||||
|
"github.com/rulego/streamsql/dataset"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
"github.com/rulego/streamsql/utils/cast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AggregateOp struct {
|
||||||
|
BaseOp
|
||||||
|
//WindowType rsql.WindowType
|
||||||
|
AggregateFunc *rsql.FunctionCall
|
||||||
|
//dataList *queue.Queue
|
||||||
|
//groupDataList map[types.GroupFields]*queue.Queue
|
||||||
|
ArgsProgram []*vm.Program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AggregateOp) Init(context types.StreamSqlContext) error {
|
||||||
|
//o.groupDataList = make(map[types.GroupFields]*queue.Queue)
|
||||||
|
|
||||||
|
//o.dataList = queue.NewCircleQueue(10000)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AggregateOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
if ctx, ok := context.(types.SelectStreamSqlContext); ok {
|
||||||
|
for index, arg := range o.AggregateFunc.Args {
|
||||||
|
fieldId := arg.(*rsql.ExpressionLang).Val
|
||||||
|
if v, ok := ctx.GetColumn(ctx.GetCurrentGroupValues(), dataset.Key(fieldId)); ok {
|
||||||
|
ctx.AddFieldAggregateValue(ctx.GetCurrentGroupValues(), fieldId, cast.ToFloat(v))
|
||||||
|
} else {
|
||||||
|
if result, err := o.eval(o.ArgsProgram[index], ctx.InputAsMap()); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
ctx.AddColumn(ctx.GetCurrentGroupValues(), dataset.Float64(fieldId, cast.ToFloat(result)))
|
||||||
|
ctx.AddFieldAggregateValue(ctx.GetCurrentGroupValues(), fieldId, cast.ToFloat(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler 窗口添加数据事件
|
||||||
|
func (o *AggregateOp) AddHandler(context types.StreamSqlOperatorContext, data float64) {
|
||||||
|
fmt.Println(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchiveHandler 清除原始数据,观察者需要保存中间过程
|
||||||
|
func (o *AggregateOp) ArchiveHandler(context types.StreamSqlOperatorContext, dataList []float64) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartHandler 窗口开始事件
|
||||||
|
func (o *AggregateOp) StartHandler(context types.StreamSqlOperatorContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndHandler 窗口结束事件
|
||||||
|
func (o *AggregateOp) EndHandler(context types.StreamSqlOperatorContext, dataList []float64) {
|
||||||
|
if f, ok := builtin.AggregateBuiltins[o.AggregateFunc.Name]; ok {
|
||||||
|
|
||||||
|
fieldId := o.AggregateFunc.Args[0].(*rsql.ExpressionLang).Val
|
||||||
|
values := context.GetFieldAggregateValue(fieldId)
|
||||||
|
result, err := f.Func(values)
|
||||||
|
fmt.Println(result, err)
|
||||||
|
//o.dataList.Reset()
|
||||||
|
}
|
||||||
|
}
|
17
operator/alias_op.go
Normal file
17
operator/alias_op.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
42
operator/filter_op.go
Normal file
42
operator/filter_op.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/expr-lang/expr/vm"
|
||||||
|
types2 "github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FilterOp struct {
|
||||||
|
BaseOp
|
||||||
|
Program *vm.Program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *FilterOp) Init(context types2.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *FilterOp) Apply(context types2.StreamSqlContext) error {
|
||||||
|
if ctx, ok := context.(types2.SelectStreamSqlContext); ok {
|
||||||
|
if result, err := o.eval(o.Program, ctx.InputAsMap()); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !AsBool(result) {
|
||||||
|
return types2.ErrNotMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
21
operator/function_op.go
Normal file
21
operator/function_op.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
type FunctionOp struct {
|
||||||
|
*BaseOp
|
||||||
|
}
|
77
operator/group_by_op.go
Normal file
77
operator/group_by_op.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
"github.com/rulego/streamsql/utils/cast"
|
||||||
|
)
|
||||||
|
|
||||||
|
//// CreateGroupByKey 创建GroupByKey
|
||||||
|
//func CreateGroupByKey(fields ...string) GroupFields {
|
||||||
|
// return fields
|
||||||
|
//}
|
||||||
|
|
||||||
|
type GroupByOp struct {
|
||||||
|
*BaseOp
|
||||||
|
GroupByKey types.GroupFields
|
||||||
|
keys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *GroupByOp) Init(context types.StreamSqlContext) error {
|
||||||
|
o.keys = o.GroupByKey.ToList()
|
||||||
|
//if selectStreamSqlContext, ok := context.(types.StreamSqlOperatorContext); ok {
|
||||||
|
// selectStreamSqlContext.SetGroupByKey(o.GroupByKey)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *GroupByOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
if selectStreamSqlContext, ok := context.(types.SelectStreamSqlContext); ok {
|
||||||
|
if groupValues, ok := o.getGroupValues(selectStreamSqlContext.InputAsMap()); ok {
|
||||||
|
selectStreamSqlContext.SetCurrentGroupValues(groupValues)
|
||||||
|
} else {
|
||||||
|
selectStreamSqlContext.SetCurrentGroupValues(types.EmptyGroupValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *GroupByOp) getGroupValues(data map[string]interface{}) (types.GroupValues, bool) {
|
||||||
|
var list []string
|
||||||
|
for _, key := range o.keys {
|
||||||
|
if v, ok := data[key]; ok {
|
||||||
|
list = append(list, cast.ToString(v))
|
||||||
|
} else {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.NewGroupValues(list...), true
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 检查是否是当前分组数据
|
||||||
|
//func (o *GroupByOp) checkIsGroup(data map[string]interface{}) bool {
|
||||||
|
// if data == nil {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// for _, key := range o.keys {
|
||||||
|
// if _, ok := data[key]; ok {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return false
|
||||||
|
//}
|
17
operator/having_op.go
Normal file
17
operator/having_op.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
35
operator/limit_op.go
Normal file
35
operator/limit_op.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LimitOp struct {
|
||||||
|
*BaseOp
|
||||||
|
Limit *rsql.Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LimitOp) Init(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LimitOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
50
operator/lookup_field_op.go
Normal file
50
operator/lookup_field_op.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/expr-lang/expr/vm"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LookupFieldOp struct {
|
||||||
|
*BaseOp
|
||||||
|
Field EvalField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LookupFieldOp) Init(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LookupFieldOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
if ctx, ok := context.(types.SelectStreamSqlContext); ok {
|
||||||
|
if o.Field.EvalProgram != nil {
|
||||||
|
if result, err := o.eval(o.Field.EvalProgram, ctx.InputAsMap()); err == nil {
|
||||||
|
ctx.AddField(o.Field.FieldId, result)
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EvalField struct {
|
||||||
|
EvalProgram *vm.Program
|
||||||
|
FieldId string
|
||||||
|
Alias string
|
||||||
|
}
|
37
operator/operator.go
Normal file
37
operator/operator.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/expr-lang/expr"
|
||||||
|
"github.com/expr-lang/expr/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseOp struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *BaseOp) eval(program *vm.Program, env interface{}) (any, error) {
|
||||||
|
return expr.Run(program, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsBool convert any to bool
|
||||||
|
func AsBool(input any) bool {
|
||||||
|
if v, ok := input.(bool); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
35
operator/order_op.go
Normal file
35
operator/order_op.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderByOp struct {
|
||||||
|
*BaseOp
|
||||||
|
OrderBy []rsql.OrderByItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OrderByOp) Init(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OrderByOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
35
operator/table_op.go
Normal file
35
operator/table_op.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TableOp struct {
|
||||||
|
*BaseOp
|
||||||
|
From []rsql.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableOp) Init(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
51
operator/window_op.go
Normal file
51
operator/window_op.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
"github.com/rulego/streamsql/window"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WindowOp struct {
|
||||||
|
*BaseOp
|
||||||
|
WindowType rsql.WindowType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *WindowOp) Init(context types.StreamSqlContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *WindowOp) Apply(context types.StreamSqlContext) error {
|
||||||
|
if ctx, ok := context.(types.SelectStreamSqlContext); ok && !ctx.IsInitWindow(ctx.GetCurrentGroupValues()) {
|
||||||
|
var w types.Window
|
||||||
|
switch o.WindowType {
|
||||||
|
case rsql.TUMBLING_WINDOW:
|
||||||
|
w = window.NewTumblingWindow(ctx, "", 10*time.Second, ctx.CreteWindowObserver())
|
||||||
|
case rsql.COUNT_WINDOW:
|
||||||
|
w = window.NewCountWindow("", 100, ctx.CreteWindowObserver())
|
||||||
|
case rsql.SLIDING_WINDOW:
|
||||||
|
w = window.NewSlidingWindow("", 10*time.Second, 5*time.Second, ctx.CreteWindowObserver())
|
||||||
|
case rsql.SESSION_WINDOW:
|
||||||
|
w = window.NewSessionWindow("", 10*time.Second, ctx.CreteWindowObserver())
|
||||||
|
}
|
||||||
|
ctx.AddWindow(ctx.GetCurrentGroupValues(), w)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
54
planner/filter_plan.go
Normal file
54
planner/filter_plan.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package planner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/expr-lang/expr"
|
||||||
|
"github.com/rulego/streamsql/operator"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FilterPlan struct {
|
||||||
|
*BaseLogicalPlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FilterPlan) New() types.LogicalPlan {
|
||||||
|
return &FilterPlan{BaseLogicalPlan: &BaseLogicalPlan{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FilterPlan) Plan(statement rsql.Statement) error {
|
||||||
|
if selectStatement, ok := statement.(*rsql.Select); ok {
|
||||||
|
if expressionLang, ok := selectStatement.Where.(*rsql.ExpressionLang); ok {
|
||||||
|
code := expressionLang.Val
|
||||||
|
if program, err := expr.Compile(code); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.AddOperators(&operator.FilterOp{Program: program})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FilterPlan) Explain() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FilterPlan) Type() string {
|
||||||
|
return "FilterPlan"
|
||||||
|
}
|
56
planner/group_by_plan.go
Normal file
56
planner/group_by_plan.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package planner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/operator"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GroupByPlan struct {
|
||||||
|
*BaseLogicalPlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GroupByPlan) New() types.LogicalPlan {
|
||||||
|
return &GroupByPlan{BaseLogicalPlan: &BaseLogicalPlan{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GroupByPlan) Plan(statement rsql.Statement) error {
|
||||||
|
if selectStatement, ok := statement.(*rsql.Select); ok {
|
||||||
|
for _, item := range selectStatement.GroupBy {
|
||||||
|
if functionCallField, ok := item.(*rsql.FunctionCall); ok && functionCallField.IsWindow {
|
||||||
|
p.AddOperators(&operator.WindowOp{
|
||||||
|
WindowType: rsql.LookupIsWindowFunc(functionCallField.Name),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
p.AddOperators(&operator.GroupByOp{
|
||||||
|
GroupByKey: types.GroupFields(item.(*rsql.Identifier).Val),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GroupByPlan) Explain() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GroupByPlan) Type() string {
|
||||||
|
return "GroupByPlan"
|
||||||
|
}
|
48
planner/limit_plan.go
Normal file
48
planner/limit_plan.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package planner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/operator"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LimitPlan struct {
|
||||||
|
*BaseLogicalPlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LimitPlan) New() types.LogicalPlan {
|
||||||
|
return &LimitPlan{BaseLogicalPlan: &BaseLogicalPlan{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LimitPlan) Plan(statement rsql.Statement) error {
|
||||||
|
if selectStatement, ok := statement.(*rsql.Select); ok {
|
||||||
|
p.AddOperators(&operator.LimitOp{
|
||||||
|
Limit: selectStatement.Limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LimitPlan) Explain() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LimitPlan) Type() string {
|
||||||
|
return "LimitPlan"
|
||||||
|
}
|
93
planner/look_field_plan.go
Normal file
93
planner/look_field_plan.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package planner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/expr-lang/expr"
|
||||||
|
"github.com/expr-lang/expr/vm"
|
||||||
|
"github.com/rulego/streamsql/operator"
|
||||||
|
"github.com/rulego/streamsql/rsql"
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LookFieldPlan struct {
|
||||||
|
*BaseLogicalPlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LookFieldPlan) New() types.LogicalPlan {
|
||||||
|
return &LookFieldPlan{BaseLogicalPlan: &BaseLogicalPlan{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LookFieldPlan) Plan(statement rsql.Statement) error {
|
||||||
|
if selectStatement, ok := statement.(*rsql.Select); ok {
|
||||||
|
for _, field := range selectStatement.SelectExprs {
|
||||||
|
if functionCallField, ok := field.Expr.(*rsql.FunctionCall); ok && functionCallField.IsAggregate {
|
||||||
|
if len(functionCallField.Args) > 0 {
|
||||||
|
code, ok := functionCallField.Args[0].(*rsql.ExpressionLang)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if program, err := expr.Compile(code.Val); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
//p.AddOperators(&operator.LookupFieldOp{
|
||||||
|
// Field: operator.EvalField{
|
||||||
|
// EvalProgram: program,
|
||||||
|
// FieldId: code.Val,
|
||||||
|
// },
|
||||||
|
//})
|
||||||
|
p.AddOperators(&operator.AggregateOp{
|
||||||
|
AggregateFunc: functionCallField,
|
||||||
|
ArgsProgram: []*vm.Program{program},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
p.AddOperators(&operator.AggregateOp{
|
||||||
|
AggregateFunc: functionCallField,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
code, ok := field.Expr.(*rsql.Identifier)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if program, err := expr.Compile(code.Val); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.AddOperators(&operator.LookupFieldOp{
|
||||||
|
Field: operator.EvalField{
|
||||||
|
EvalProgram: program,
|
||||||
|
FieldId: code.Val,
|
||||||
|
Alias: field.Alias,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LookFieldPlan) Explain() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LookFieldPlan) Type() string {
|
||||||
|
return "LookFieldPlan"
|
||||||
|
}
|
26
planner/optimizer.go
Normal file
26
planner/optimizer.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The RuleGo Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package planner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rulego/streamsql/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Optimize 优化
|
||||||
|
func Optimize(p types.LogicalPlan) (types.LogicalPlan, error) {
|
||||||
|
return p, nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user