Check if key exists in object in Rego

Saturday, September 28, 2019

Recently, I'm writing Rego(a query language to use in Open Policy Agent) every day and have decided to post some tricky syntaxes that took me a little time to understand.

In this post, I'm going to look at how to check if a key exists in an object in Rego.

I've pulled the sample code from the Policy Cheatsheet in the official docs.

has_key(x, k) { _ = x[k] }

Looks simple, but has a bit of a gotcha (at least for me) despite consuming the function is pretty intuitive (here's the playground):

has_key({"foo": "bar"}, "foo") # true
has_key({"foo": "bar"}, "baz") # undefined

It's checking if key k exists in object x . What confused me at first is _ = x[k] . Why do we need the _ = part?

Without the "_ ="

Let's see what happens if the function becomes like this:

has_key(x, k) { x[k] }

At first, this looks like it's working. But you have to think about the case where the value of x[k] is actuallyfalse .

The return value of the function is true whenever the function body is satisfied. That is the case when x[k] unifies to anything _that is not false _ -- if x[k] unifies to false , the function body is not satisfied... (functions are not very different from rules). Here's an example(and the playground):

has_key(x, k) { x[k] }

default foo_exists = false
foo_exists = has_key({"foo": false}, "foo") # false!!!

To avoid this unexpected behavior, the assigning(or union) part ( _ = ) is mandatory.

With the "_ ="

Let's put the _ = back inside the function.

has_key(x, k) { _ = x[k] }

With this, the function body is satisfied if it unifies -- i.e. there's an x[k], not undefined -- but it doesn't matter if it's true or false , because the x[k] is put into a context where its value doesn't matter: _ = y never fails, regardless of the value of y, as long as y is notundefined .

Any other construct that doesn't regard the value would work as well. Here are some alternatives (which are not better, just different):

Wrap up

Again, you can check the correct version in this playground.

If you're still uncomfortable with the Rego syntax used inside the function, check out the Policy Language# Variable Keys section in the official docs. Also, you can check the Policy Cheatsheet# Objects section.

Happy Rego coding!

P.S. I'd like to greatly appreciate @srenatus from the OPA community for reviewing my post and giving me accurate advice!

This post is also available on DEV.