Skip to content

Commit

Permalink
✨ dict.containsNone support (#1234)
Browse files Browse the repository at this point in the history
After: #1233 !!

the counter-part for containsOnly for dicts (parsed json etc)

Signed-off-by: Dominik Richter <[email protected]>
  • Loading branch information
arlimus authored May 23, 2023
1 parent 55adc59 commit 3a14d30
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 6 deletions.
1 change: 1 addition & 0 deletions llx/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ func init() {
"$one": {f: dictOneV2},
"map": {f: dictMapV2},
"difference": {f: dictDifferenceV2},
"containsNone": {f: dictContainsNoneV2},
string("contains" + types.String): {f: dictContainsStringV2, Label: "contains"},
string("contains" + types.Array(types.String)): {f: dictContainsArrayStringV2, Label: "contains"},
string("contains" + types.Int): {f: dictContainsIntV2, Label: "contains"},
Expand Down
51 changes: 51 additions & 0 deletions llx/builtin_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,57 @@ func dictDifferenceV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64)
return &RawData{Type: bind.Type, Value: res}, 0, nil
}

func dictContainsNoneV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) {
if bind.Value == nil {
return &RawData{Type: bind.Type, Error: bind.Error}, 0, nil
}

args := chunk.Function.Args
// TODO: all this needs to go into the compile phase
if len(args) < 1 {
return nil, 0, errors.New("Called `difference` with " + strconv.Itoa(len(args)) + " arguments, only 1 supported.")
}
if len(args) > 1 {
return nil, 0, errors.New("called `difference` with " + strconv.Itoa(len(args)) + " arguments, only 1 supported.")
}
// ^^ TODO

argRef := args[0]
arg, rref, err := e.resolveValue(argRef, ref)
if err != nil || rref > 0 {
return nil, rref, err
}

org, ok := bind.Value.([]interface{})
if !ok {
return &RawData{Type: bind.Type, Error: errors.New("cannot compute difference of lists, argument is not a list")}, 0, nil
}

filters, ok := arg.Value.([]interface{})
if !ok {
return &RawData{Type: bind.Type, Error: errors.New("tried to call function with a non-array, please make sure the argument is an array")}, 0, nil
// filters = []interface{}{arg.Value}
}

var res []interface{}
var skip bool
for i := range org {
skip = true
for j := range filters {
if org[i] == filters[j] {
skip = false
break
}
}

if !skip {
res = append(res, org[i])
}
}

return &RawData{Type: bind.Type, Value: res}, 0, nil
}

func anyContainsString(an interface{}, s string) bool {
if an == nil {
return false
Expand Down
12 changes: 6 additions & 6 deletions mqlc/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ func init() {
"where": {compile: compileDictWhere, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"contains": {compile: compileDictContains, typ: boolType, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"containsOnly": {compile: compileDictContainsOnly, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
// "containsNone": {compile: compileDictContainsNone, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"all": {compile: compileDictAll, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"any": {compile: compileDictAny, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"one": {compile: compileDictOne, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"none": {compile: compileDictNone, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"map": {compile: compileArrayMap, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"containsNone": {compile: compileDictContainsNone, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"all": {compile: compileDictAll, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"any": {compile: compileDictAny, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"one": {compile: compileDictOne, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"none": {compile: compileDictNone, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
"map": {compile: compileArrayMap, signature: FunctionSignature{Required: 1, Args: []types.Type{types.FunctionLike}}},
// map-ish
"keys": {typ: stringArrayType, signature: FunctionSignature{}},
"values": {typ: dictArrayType, signature: FunctionSignature{}},
Expand Down
61 changes: 61 additions & 0 deletions mqlc/builtin_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,67 @@ func compileDictContainsOnly(c *compiler, typ types.Type, ref uint64, id string,
return types.Bool, nil
}

func compileDictContainsNone(c *compiler, typ types.Type, ref uint64, id string, call *parser.Call) (types.Type, error) {
if call == nil || len(call.Function) != 1 {
return types.Nil, errors.New("function " + id + " needs one argument (dict)")
}

f := call.Function[0]
if f.Value == nil || f.Value.Operand == nil {
return types.Nil, errors.New("function " + id + " needs one argument (dict)")
}

val, err := c.compileOperand(f.Value.Operand)
if err != nil {
return types.Nil, err
}

valType, err := c.dereferenceType(val)
if err != nil {
return types.Nil, err
}

chunkId := "==" + string(typ)
if valType != typ {
chunkId = "==" + string(valType)
_, err := llx.BuiltinFunctionV2(typ, chunkId)
if err != nil {
return types.Nil, errors.New("called '" + id + "' with wrong type; either provide a type " + typ.Label() + " value or write it as an expression (e.g. \"_ == 123\")")
}
}

// .containsNone
c.addChunk(&llx.Chunk{
Call: llx.Chunk_FUNCTION,
Id: "containsNone",
Function: &llx.Function{
Type: string(typ),
Binding: ref,
Args: []*llx.Primitive{
val,
},
},
})

// == []
c.addChunk(&llx.Chunk{
Call: llx.Chunk_FUNCTION,
Id: chunkId,
Function: &llx.Function{
Type: string(types.Bool),
Binding: c.tailRef(),
Args: []*llx.Primitive{
llx.ArrayPrimitive([]*llx.Primitive{}, typ.Child()),
},
},
})

checksum := c.Result.CodeV2.Checksums[c.tailRef()]
c.Result.Labels.Labels[checksum] = "[].containsNone()"

return types.Bool, nil
}

func compileDictAll(c *compiler, typ types.Type, ref uint64, id string, call *parser.Call) (types.Type, error) {
_, err := compileDictWhere(c, typ, ref, "$whereNot", call)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions resources/packs/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,14 @@ func TestDict_Methods_Contains(t *testing.T) {
// p + "params['string-array'].containsOnly('a')",
// 1, false,
// },
{
p + "params['string-array'].containsNone(['d','e'])",
1, true,
},
{
p + "params['string-array'].containsNone(['a', 'e'])",
1, false,
},
{
p + "params['string-array'].none('a')",
1, false,
Expand Down

0 comments on commit 3a14d30

Please sign in to comment.