3
0

Add support for in and nin ref handlers in reporter joins

This commit is contained in:
Tomaž Jerman
2022-11-23 16:07:19 +01:00
committed by Jože Fortun
parent 8f3c3c3ea7
commit 107d7a70ce
4 changed files with 117 additions and 17 deletions
+44 -11
View File
@@ -219,24 +219,41 @@ func (def *Link) init(ctx context.Context, left, right Iterator) (exec *linkLeft
}
func (r *rowLink) SelectGVal(ctx context.Context, k string) (interface{}, error) {
return r.GetValue(k, 0)
// @todo this way of determining multivalue is not entirely ok but we can get away with it
// since we have special functions to work with those.
// We need to somehow inform this bit as to what fields are multi value.
row, counts := r.getGetter(k)
if row == nil {
return nil, nil
}
cc := counts[k]
if cc == 0 {
return nil, nil
}
if cc == 1 {
return row.GetValue(k, 0)
}
out := make([]any, cc)
for i := uint(0); i < cc; i++ {
out[i], _ = row.GetValue(k, i)
}
return out, nil
}
func (r *rowLink) GetValue(name string, pos uint) (v any, err error) {
a := r.a.CountValues()
row, a := r.getGetter(name)
if row == nil {
return
}
if cc, ok := a[name]; ok {
if pos >= cc {
return nil, nil
}
return r.a.GetValue(name, pos)
}
b := r.b.CountValues()
if cc, ok := b[name]; ok {
if pos >= cc {
return nil, nil
}
return r.b.GetValue(name, pos)
return row.GetValue(name, pos)
}
return
@@ -254,3 +271,19 @@ func (r *rowLink) CountValues() (out map[string]uint) {
return
}
// getGetter returns the ValueGetter based on if the value is in a or b
// If neither, it returns nil
func (r *rowLink) getGetter(k string) (row ValueGetter, cc map[string]uint) {
cc = r.a.CountValues()
if _, ok := cc[k]; ok {
return r.a, cc
}
cc = r.b.CountValues()
if _, ok := cc[k]; ok {
return r.b, cc
}
return nil, nil
}
+51 -2
View File
@@ -3,9 +3,11 @@ package dal
import (
"context"
"fmt"
"reflect"
"strings"
"github.com/PaesslerAG/gval"
"github.com/cortezaproject/corteza/server/pkg/expr"
"github.com/cortezaproject/corteza/server/pkg/gvalfnc"
"github.com/cortezaproject/corteza/server/pkg/ql"
)
@@ -125,6 +127,21 @@ var (
OutTypeUnknown: true,
},
"in": {
Handler: func(args ...string) string {
// The arguments must be reversed!!
return fmt.Sprintf("has(%s, %s)", args[1], args[0])
},
OutTypeUnknown: true,
},
"nin": {
Handler: func(args ...string) string {
// The arguments must be reversed!!
return fmt.Sprintf("!has(%s, %s)", args[1], args[0])
},
OutTypeUnknown: true,
},
// - strings
"concat": {
Handler: func(args ...string) string {
@@ -311,7 +328,7 @@ func newRunnerGvalParsed(n *ql.ASTNode) (out *runnerGval, err error) {
// @note the subset is limited to simplify the (eventual) offloading to the DB.
// At some point, more functions will be supported, and the ones which can't
// be offloaded will be performed in some exec. step.
func newGval(expr string) (gval.Evaluable, error) {
func newGval(e string) (gval.Evaluable, error) {
return gval.Full(
// Extra functions we'll need
// @note don't bring in all of the expr. pkg functions as we'll need to
@@ -328,7 +345,8 @@ func newGval(expr string) (gval.Evaluable, error) {
gval.Function("int", gvalfnc.CastInt),
gval.Function("string", gvalfnc.CastString),
gval.Function("concat", gvalfnc.ConcatStrings),
).NewEvaluable(expr)
gval.Function("has", arrHas),
).NewEvaluable(e)
}
func newQlParser(onIdent ...ql.IdentHandler) *ql.Parser {
@@ -409,3 +427,34 @@ func (c converterGval) refHandler(n *ql.ASTNode, args ...string) (out string, er
}
return refToGvalExp[r].Handler(args...), nil
}
// arrHas is a helper to assure the expr.Has always gets an array (or map) as
// the first argument
//
// @todo this is needed because how the ValueGetters returns multi-value fields so
// an edge case where a field would have [a] but here, it would be presented
// as a.
// This would become obsolete when we address the actual issue.
func arrHas(arr interface{}, vv ...interface{}) (b bool, err error) {
arr = expr.UntypedValue(arr)
if isMap(arr) {
return expr.Has(arr, vv...)
}
var (
c = reflect.ValueOf(arr)
)
switch c.Kind() {
case reflect.Slice:
return expr.Has(arr, vv...)
default:
return expr.Has([]any{arr}, vv...)
}
}
func isMap(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Map
}
+20 -2
View File
@@ -57,8 +57,26 @@ func (r *Row) WithValue(name string, pos uint, v any) *Row {
return r
}
func (r Row) SelectGVal(ctx context.Context, k string) (interface{}, error) {
return r.GetValue(unwrapNestedGvalIdent(k), 0)
func (r Row) SelectGVal(ctx context.Context, wk string) (interface{}, error) {
// @todo this way of determining multivalue is not entirely ok but we can get away with it
// since we have special functions to work with those.
// We need to somehow inform this bit as to what fields are multi value.
k := unwrapNestedGvalIdent(wk)
cc := r.CountValues()[k]
if cc == 0 {
return nil, nil
}
if cc == 1 {
return r.GetValue(k, 0)
}
out := make([]any, cc)
for i := uint(0); i < cc; i++ {
out[i], _ = r.GetValue(k, i)
}
return out, nil
}
// Reset clears out the row so the same instance can be reused where possible
+2 -2
View File
@@ -15,7 +15,7 @@ func ArrayFunctions() []gval.Language {
gval.Function("pop", pop),
gval.Function("shift", shift),
gval.Function("count", count),
gval.Function("has", has),
gval.Function("has", Has),
gval.Function("hasAll", hasAll),
gval.Function("find", find),
gval.Function("sort", sortSlice),
@@ -157,7 +157,7 @@ func count(arr interface{}, v ...interface{}) (count int, err error) {
}
// has finds any occurrence of the values in slice or key in a map
func has(arr interface{}, vv ...interface{}) (b bool, err error) {
func Has(arr interface{}, vv ...interface{}) (b bool, err error) {
arr = UntypedValue(arr)
if isMap(arr) {