Skip to content

Commit

Permalink
built-ins: add object.subset() builtin
Browse files Browse the repository at this point in the history
This implements the new object.subset() builtin.

Based on my benchmarking, this offers a 2.77x speedup compared to
implementing the same thing in pure Rego, and is also easier to read.

Fixes #4358

Signed-off-by: Charles Daniels <charles@styra.com>
  • Loading branch information
charlesdaniels authored and ashutosh-narkar committed Jun 9, 2022
1 parent f137da2 commit f2fd8f5
Show file tree
Hide file tree
Showing 6 changed files with 825 additions and 9 deletions.
52 changes: 43 additions & 9 deletions ast/builtins.go
Expand Up @@ -161,6 +161,7 @@ var DefaultBuiltins = [...]*Builtin{
ObjectRemove,
ObjectFilter,
ObjectGet,
ObjectSubset,

// JSON Object Manipulation
JSONFilter,
Expand Down Expand Up @@ -1425,18 +1426,36 @@ var JSONPatch = &Builtin{
Categories: objectCat,
}

var ObjectGet = &Builtin{
Name: "object.get",
Description: "Returns value of an object's key if present, otherwise a default. " +
"If the supplied `key` is an `array`, then `object.get` will search through a nested object or array using each key in turn. " +
"For example: `object.get({\"a\": [{ \"b\": true }]}, [\"a\", 0, \"b\"], false)` results in `true`.",
var ObjectSubset = &Builtin{
Name: "object.subset",
Description: "Determines if an object `sub` is a subset of another object `super`." +
"Object `sub` is a subset of object `super` if and only if every key in `sub` is also in `super`, " +
"**and** for all keys which `sub` and `super` share, they have the same value. " +
"This function works with objects, sets, and arrays. " +
"If both arguments are objects, then the operation is recursive, e.g. " +
"`{\"c\": {\"x\": {10, 15, 20}}` is a subset of `{\"a\": \"b\", \"c\": {\"x\": {10, 15, 20, 25}, \"y\": \"z\"}`. " +
"If both arguments are sets, then this function checks if every element of `sub` is a member of `super`, " +
"but does not attempt to recurse. If both arguments are arrays, " +
"then this function checks if `sub` appears contiguously in order within `super`, " +
"and also does not attempt to recurse.",
Decl: types.NewFunction(
types.Args(
types.Named("object", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("object to get `key` from"),
types.Named("key", types.A).Description("key to lookup in `object`"),
types.Named("default", types.A).Description("default to use when lookup fails"),
types.Named("super", types.NewAny(types.NewObject(
nil,
types.NewDynamicProperty(types.A, types.A),
),
types.NewSet(types.A),
types.NewArray(nil, types.A),
)).Description("object to test if sub is a subset of"),
types.Named("sub", types.NewAny(types.NewObject(
nil,
types.NewDynamicProperty(types.A, types.A),
),
types.NewSet(types.A),
types.NewArray(nil, types.A),
)).Description("object to test if super is a superset of"),
),
types.Named("value", types.A).Description("`object[key]` if present, otherwise `default`"),
types.Named("result", types.A).Description("`true` if `sub` is a subset of `super`"),
),
}

Expand Down Expand Up @@ -1513,6 +1532,21 @@ var ObjectFilter = &Builtin{
),
}

var ObjectGet = &Builtin{
Name: "object.get",
Description: "Returns value of an object's key if present, otherwise a default. " +
"If the supplied `key` is an `array`, then `object.get` will search through a nested object or array using each key in turn. " +
"For example: `object.get({\"a\": [{ \"b\": true }]}, [\"a\", 0, \"b\"], false)` results in `true`.",
Decl: types.NewFunction(
types.Args(
types.Named("object", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("object to get `key` from"),
types.Named("key", types.A).Description("key to lookup in `object`"),
types.Named("default", types.A).Description("default to use when lookup fails"),
),
types.Named("value", types.A).Description("`object[key]` if present, otherwise `default`"),
),
}

/*
* Encoding
*/
Expand Down
26 changes: 26 additions & 0 deletions builtin_metadata.json
Expand Up @@ -118,6 +118,7 @@
"object.filter",
"object.get",
"object.remove",
"object.subset",
"object.union",
"object.union_n"
],
Expand Down Expand Up @@ -8394,6 +8395,31 @@
},
"wasm": true
},
"object.subset": {
"args": [
{
"description": "object to test if sub is a subset of",
"name": "super",
"type": "any\u003carray[any], object[any: any], set[any]\u003e"
},
{
"description": "object to test if super is a superset of",
"name": "sub",
"type": "any\u003carray[any], object[any: any], set[any]\u003e"
}
],
"available": [
"edge"
],
"description": "Determines if an object `sub` is a subset of another object `super`.Object `sub` is a subset of object `super` if and only if every key in `sub` is also in `super`, **and** for all keys which `sub` and `super` share, they have the same value. This function works with objects, sets, and arrays. If both arguments are objects, then the operation is recursive, e.g. `{\"c\": {\"x\": {10, 15, 20}}` is a subset of `{\"a\": \"b\", \"c\": {\"x\": {10, 15, 20, 25}, \"y\": \"z\"}`. If both arguments are sets, then this function checks if every element of `sub` is a member of `super`, but does not attempt to recurse. If both arguments are arrays, then this function checks if `sub` appears contiguously in order within `super`, and also does not attempt to recurse.",
"introduced": "edge",
"result": {
"description": "`true` if `sub` is a subset of `super`",
"name": "result",
"type": "any"
},
"wasm": false
},
"object.union": {
"args": [
{
Expand Down
67 changes: 67 additions & 0 deletions capabilities.json
Expand Up @@ -2671,6 +2671,73 @@
"type": "function"
}
},
{
"name": "object.subset",
"decl": {
"args": [
{
"of": [
{
"dynamic": {
"type": "any"
},
"type": "array"
},
{
"dynamic": {
"key": {
"type": "any"
},
"value": {
"type": "any"
}
},
"type": "object"
},
{
"of": {
"type": "any"
},
"type": "set"
}
],
"type": "any"
},
{
"of": [
{
"dynamic": {
"type": "any"
},
"type": "array"
},
{
"dynamic": {
"key": {
"type": "any"
},
"value": {
"type": "any"
}
},
"type": "object"
},
{
"of": {
"type": "any"
},
"type": "set"
}
],
"type": "any"
}
],
"result": {
"type": "any"
},
"type": "function"
}
},
{
"name": "object.union",
"decl": {
Expand Down

0 comments on commit f2fd8f5

Please sign in to comment.