Unknown variables

251 views
Skip to first unread message

Rodrigo Trujillo

unread,
Sep 28, 2020, 11:40:24 PM9/28/20
to CEL Go Discussion Forum
Hello guys,

I am wondering if there is a way to assign NULL to known variables.
For instance see following example code:

env, _ := cel.NewEnv()
expr := `test + " rodrigo " + test2`
ast, _ := env.Parse(expr)

prg, _ := env.Program(ast)
row := map[string]interface{}{
    // "test":  "Mr.",
    "test2": "truj",
}

outField, evalDetails, err := prg.Eval(row)

fmt.Println(outField))
fmt.Println(evalDetails)
fmt.Println(err)

Notice that "test" is commented, so the variable will not be passed to the expression during evaluation.
This code gives me following error:

no such attribute: id: 1, attributes: [id: 1, names: [test]]

I wonder if there is a way to make cel automatically assign NULL to 'test' instead of return error, and get an output like:
" rodrigo truj"
or
"NULL rodrigo truj"

Thank you in advance,
Rodrigo
--

Rodrigo Trujillo

SOFTWARE DEVELOPER

Auvik Networks Inc.

Jim Larson

unread,
Sep 29, 2020, 3:58:49 PM9/29/20
to Rodrigo Trujillo, CEL Go Discussion Forum
Hi, Rodrigo

Unfortunately, CEL really wants you to declare and bind all the variables that you use in an expression.  We have a mechanism for partial evaluation if some inputs are unknown or deferred because they are expensive, but it wouldn't compute the string concatenation if some of the terms are missing.  Because of partial evaluation, we don't even allow you to test if a variable is bound or not, to prevent the behavior from changing in partial evaluation settings.

For the particular example you give, it seems like the right thing to do is to always bind "test" to some string, using the empty string if you don't have a better value.  This will give the first option on the output.  If you want the second option, you can test "test" for the empty string in your expression:

(test == "" ? "NULL" : test) + " rodrigo " + test2

If you need to distinguish an intentional binding of the empty string as an intended value vs a default, then you can introduce a boolean variable to distinguish the cases:

(has_test ? test : "NULL") + " rodrigo " + test2

Let us know if your use case isn't satisfied with one of the suggestions above.

Jim


--
You received this message because you are subscribed to the Google Groups "CEL Go Discussion Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cel-go-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cel-go-discuss/CACD2JJSG3pGVGagJoXm631SBnT90oxYDrP8cX_fxdrGwMtR%3Dxg%40mail.gmail.com.

Rodrigo Trujillo

unread,
Sep 29, 2020, 4:54:40 PM9/29/20
to CEL Go Discussion Forum
Thank you for the answers Tristan and Jim,

I think my example was very simple :)
Actually the expression I am trying to evaluate looks like:

 (roleStandBy == 2 || roleStandBy == 3) ? "4" : roleMgmtAdmin == 1 ? "1" : roleMgmtAdmin == 2 ? "2" : "3"'

So, I am checking both variables, but roleStandBy has priority over roleMgmtAdmin.
Turns out that we are not using variable bindings,  and sometimes roleStandBy will not be discovered and passed to the expression evaluation. This is making the code fail.

Then I thought about some solutions:
- have cel assigning NULL to the missing variable and change the expression to catch this (per our messages above, looks like it is not possible)
- change the code that create the parameters and pass nil to any missing one (and change the expression), for instance:
{ "rolesnatdBy": nil, "roleMgmtAdmin": 2}

I am testing the second option at this moment.
Let me know if you have any other idea/tip  =]

Rodrigo

Jim Larson

unread,
Sep 29, 2020, 7:30:13 PM9/29/20
to Rodrigo Trujillo, CEL Go Discussion Forum
Rodrigo,

It looks like roleStandBy is an int, so CEL will complain unless you bind this variable to an int. (See https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/).

The easiest thing to do is to use some integer code for "unknown" or "undiscovered", e.g. -1.  If there are no int values available for this purpose, then use an auxiliary boolean variable:

 hasRoleStandBy ? ((roleStandBy == 2 || roleStandBy == 3) ? "4" : roleMgmtAdmin == 1 ? "1" : roleMgmtAdmin == 2 ? "2" : "3"')
roleMgmtAdmin == 1 ? "1" : roleMgmtAdmin == 2 ? "2" : "3"'

Jim


Rodrigo Trujillo

unread,
Sep 30, 2020, 9:12:45 AM9/30/20
to CEL Go Discussion Forum
Yeap! I am going that way.
Thanks Jim
Reply all
Reply to author
Forward
0 new messages