Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/usage/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ Syntax: `{bucket intVal "bucketSize"}`

Given a value, create equal-sized buckets and place each value in those buckets

eg. `{bucketrange 70 50}` will return `50`

#### BucketRange

Syntax: `{bucketrange intVal "bucketSize"}`

Given a value, create equal-sized buckets and place value into bucket. Outputs range of bucket.

eg. `{bucketrange 70 50}` will return `50 - 99`

#### ExpBucket

Syntax: `{expbucket intVal}`
Expand Down
1 change: 1 addition & 0 deletions pkg/expressions/stdlib/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
ErrEnum = newFuncErr(ErrorEnum, "unable to find value in set")
ErrEmpty = newFuncErr(ErrorEmpty, "invalid empty value")
ErrFile = newFuncErr(ErrorFile, "unable to read file")
ErrValue = newFuncErr(ErrorValue, "value out of range")
)

var (
Expand Down
9 changes: 5 additions & 4 deletions pkg/expressions/stdlib/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
)

var StandardFunctions = map[string]KeyBuilderFunction{
"coalesce": KeyBuilderFunction(kfCoalesce),
"bucket": KeyBuilderFunction(kfBucket),
"clamp": KeyBuilderFunction(kfClamp),
"expbucket": KeyBuilderFunction(kfExpBucket),
"coalesce": KeyBuilderFunction(kfCoalesce),
"bucket": KeyBuilderFunction(kfBucket),
"bucketrange": kfBucketRange,
"clamp": KeyBuilderFunction(kfClamp),
"expbucket": KeyBuilderFunction(kfExpBucket),

// Checks
"isint": KeyBuilderFunction(kfIsInt),
Expand Down
48 changes: 45 additions & 3 deletions pkg/expressions/stdlib/funcsCommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,63 @@ func kfBucket(args []KeyBuilderStage) (KeyBuilderStage, error) {
return stageErrArgCount(args, 2)
}

bucketSize, bucketSizeOk := EvalStageInt(args[1])
bucketSize, bucketSizeOk := EvalStageInt64(args[1])
if !bucketSizeOk {
return stageArgError(ErrNum, 1)
}
if bucketSize <= 0 {
return stageArgError(ErrValue, 1)
}

return KeyBuilderStage(func(context KeyBuilderContext) string {
val, err := strconv.Atoi(args[0](context))
val, err := strconv.ParseInt(args[0](context), 10, 64)
if err != nil {
return ErrorNum
}

return strconv.Itoa((val / bucketSize) * bucketSize)
bucket := (val / bucketSize) * bucketSize
if val < 0 {
bucket -= bucketSize
}

return strconv.FormatInt(bucket, 10)
}), nil
}

func kfBucketRange(args []KeyBuilderStage) (KeyBuilderStage, error) {
if len(args) != 2 {
return stageErrArgCount(args, 2)
}

bucketSize, bucketSizeOk := EvalStageInt64(args[1])
if !bucketSizeOk {
return stageArgError(ErrNum, 1)
}
if bucketSize <= 0 {
return stageArgError(ErrValue, 1)
}

return func(context KeyBuilderContext) string {
val, err := strconv.ParseInt(args[0](context), 10, 64)
if err != nil {
return ErrorNum
}

var start, end int64
start = (val / bucketSize) * bucketSize
if val < 0 {
start -= bucketSize
}
end = start + (bucketSize - 1)

ret := make([]byte, 0, 20)
ret = strconv.AppendInt(ret, start, 10)
ret = append(ret, " - "...)
ret = strconv.AppendInt(ret, end, 10)
return string(ret)
}, nil
}

func kfClamp(args []KeyBuilderStage) (KeyBuilderStage, error) {
if len(args) != 3 {
return stageErrArgCount(args, 3)
Expand Down
21 changes: 14 additions & 7 deletions pkg/expressions/stdlib/funcsCommon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package stdlib

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCoalesce(t *testing.T) {
Expand All @@ -14,11 +12,20 @@ func TestCoalesce(t *testing.T) {
}

func TestBucketing(t *testing.T) {
testContext := mockContext("ab", "cd", "123")
kb, _ := NewStdKeyBuilder().Compile("{bucket {2} 10} is bucketed")
key := kb.BuildKey(testContext)
assert.Equal(t, "120 is bucketed", key)
assert.Equal(t, 2, kb.StageCount())
testExpression(t, mockContext("ab", "cd", "123"), "{bucket {2} 10} is bucketed", "120 is bucketed")
testExpression(t, mockContext(), "{bucket -25 50}", "-50")
testExpressionErr(t, mockContext(), "{bucket 70 -50}", "<VALUE>", ErrValue)
testExpressionErr(t, mockContext(), "{bucket 5 a}", "<BAD-TYPE>", ErrNum)
testExpressionErr(t, mockContext(), "{bucket 5}", "<ARGN>", ErrArgCount)
}

func TestBucketRange(t *testing.T) {
testExpression(t, mockContext(), "{bucketrange 25 50}", "0 - 49")
testExpression(t, mockContext(), "{bucketrange -25 50}", "-50 - -1")
testExpression(t, mockContext(70), "{bucketrange {0} 50}", "50 - 99")
testExpressionErr(t, mockContext(), "{bucketrange 70 -50}", "<VALUE>", ErrValue)
testExpressionErr(t, mockContext(), "{bucketrange 5 a}", "<BAD-TYPE>", ErrNum)
testExpressionErr(t, mockContext(), "{bucketrange 5}", "<ARGN>", ErrArgCount)
}

func TestBucket(t *testing.T) {
Expand Down