# Rego Keyword Examples: contains

Rego's `contains` keyword is used to incrementally build [multi-value rules](https://www.openpolicyagent.org/docs/policy-language/#generating-sets) in a policy. Often, tasks like validation are defined as a series of checks and these break down nicely into a series of `contains` rules that evaluate to a larger result. A `contains` rule typically takes the following form:

```
my_rule contains value if {    # logic to check if the value should be set    # set the value    # value := ...}
```

However, there are some different ways to use `contains` in a policy which are covered in the examples below.

note

If you're looking for the built-in function `contains` for substring checking, you can read about it in the [built-ins section](/docs/policy-reference/builtins/strings#builtin-strings-contains).

## Examples

Building sets

While this first example is trivially simple and unlikely to be useful when building real policies, it illustrates the fundamental reason for using the `contains` keyword: building sets. Sets are unordered collections, and they form an important building block for many policies.

In this example, we use a multi-value rule defined using the `contains` keyword to create a simple list of todos. Just remember that sets are unordered and so you should not depend on the order of the result.

data.json

```
{}
```

input.json

```
{}
```

policy.rego

```
package playimport rego.v1todo contains "Buy bread for lunch"todo contains "Send off tax return"todo contains "Do laundry"
```

Output

{
  "todo": \[
    "Buy bread for lunch",
    "Do laundry",
    "Send off tax return"
  \]
}

Forming a list of error codes

Contains is used for creating a set of items, this list can be defined by policy and make reference to `input` or `data` and other rules.

policy.rego

```
package playimport rego.v1errors contains "E0001" if _token_expirederrors contains "E1004" if object.get(input, "email", "") == ""errors contains "E1209" if object.get(input, "claims", []) == []default _token_expired := true_token_expired if time.parse_ns("2006-01-02", input.expired_at) < time.now_ns()
```

Output

{
  "\_token\_expired": true,
  "errors": \[
    "E0001",
    "E1004",
    "E1209"
  \]
}

input.json

```
{  "e-Mail": "alice@example.com",  "claims": [],  "expired_at": "2024-06-01"}
```

data.json

```
{}
```

[Open in OPA Playground](https://play.openpolicyagent.org/?state=eyJpIjoie1xuICBcImUtTWFpbFwiOiBcImFsaWNlQGV4YW1wbGUuY29tXCIsXG4gIFwiY2xhaW1zXCI6IFtdLFxuICBcImV4cGlyZWRfYXRcIjogXCIyMDI0LTA2LTAxXCJcbn0iLCJkIjoie30iLCJwIjoicGFja2FnZSBwbGF5XG5cbmltcG9ydCByZWdvLnYxXG5cbmVycm9ycyBjb250YWlucyBcIkUwMDAxXCIgaWYgX3Rva2VuX2V4cGlyZWRcblxuZXJyb3JzIGNvbnRhaW5zIFwiRTEwMDRcIiBpZiBvYmplY3QuZ2V0KGlucHV0LCBcImVtYWlsXCIsIFwiXCIpID09IFwiXCJcblxuZXJyb3JzIGNvbnRhaW5zIFwiRTEyMDlcIiBpZiBvYmplY3QuZ2V0KGlucHV0LCBcImNsYWltc1wiLCBbXSkgPT0gW11cblxuZGVmYXVsdCBfdG9rZW5fZXhwaXJlZCA6PSB0cnVlXG5cbl90b2tlbl9leHBpcmVkIGlmIHRpbWUucGFyc2VfbnMoXCIyMDA2LTAxLTAyXCIsIGlucHV0LmV4cGlyZWRfYXQpIDwgdGltZS5ub3dfbnMoKVxuIn0=)

Aggregating validation failures

Using `contains` is commonly used to create validation messages from performing a series of checks on an object. In this example, if there are any `failures` then `allow` will be false. Using `contains` makes it possible to build up `failures` incrementally.

policy.rego

```
package playimport rego.v1default allow := falseallow if count(reasons) == 0reasons contains "name must be set" if {	object.get(input, "name", "") == ""}reasons contains "@example.com email blocked" if {	endswith(input.email, "@example.com")}reasons contains message if {	input.age < 18	message := sprintf(		"you must be %d year older",		[18 - input.age],	)}
```

Output

{
  "allow": false,
  "reasons": \[
    "@example.com email blocked",
    "name must be set",
    "you must be 1 year older"
  \]
}

input.json

```
{  "email": "name@example.com",  "age": 17}
```

data.json

```
{}
```

[Open in OPA Playground](https://play.openpolicyagent.org/?state=eyJpIjoie1xuICBcImVtYWlsXCI6IFwibmFtZUBleGFtcGxlLmNvbVwiLFxuICBcImFnZVwiOiAxN1xufSIsImQiOiJ7fSIsInAiOiJwYWNrYWdlIHBsYXlcblxuaW1wb3J0IHJlZ28udjFcblxuZGVmYXVsdCBhbGxvdyA6PSBmYWxzZVxuXG5hbGxvdyBpZiBjb3VudChyZWFzb25zKSA9PSAwXG5cbnJlYXNvbnMgY29udGFpbnMgXCJuYW1lIG11c3QgYmUgc2V0XCIgaWYge1xuXHRvYmplY3QuZ2V0KGlucHV0LCBcIm5hbWVcIiwgXCJcIikgPT0gXCJcIlxufVxuXG5yZWFzb25zIGNvbnRhaW5zIFwiQGV4YW1wbGUuY29tIGVtYWlsIGJsb2NrZWRcIiBpZiB7XG5cdGVuZHN3aXRoKGlucHV0LmVtYWlsLCBcIkBleGFtcGxlLmNvbVwiKVxufVxuXG5yZWFzb25zIGNvbnRhaW5zIG1lc3NhZ2UgaWYge1xuXHRpbnB1dC5hZ2UgPCAxOFxuXG5cdG1lc3NhZ2UgOj0gc3ByaW50Zihcblx0XHRcInlvdSBtdXN0IGJlICVkIHllYXIgb2xkZXJcIixcblx0XHRbMTggLSBpbnB1dC5hZ2VdLFxuXHQpXG59XG4ifQ==)

Validating lists of objects

Using `contains` is also useful when building up more complicated data structures in policies. In this example, `contains` is used to set validation errors that are grouped by the item they refer to.

policy.rego

```
package playimport rego.v1approval_min := 350reasons[item.id] contains "category must be set" if {	some item in input.items	object.get(item, "category", "") == ""}reasons[item.id] contains message if {	some item in input.items	item.amount > approval_min	object.get(item, "approved_by", "") == ""	message := sprintf(		"items over %d must be approved",		[approval_min],	)}reasons[item.id] contains "approver does not exist" if {	some item in input.items	not data.approvers[item.approved_by]}
```

Output

{
  "approval\_min": 350,
  "reasons": {
    "1bd91655": \[
      "category must be set"
    \],
    "226c3910": \[
      "items over 350 must be approved"
    \],
    "db5a4b23": \[
      "approver does not exist"
    \]
  }
}

input.json

```
{  "items": [    {      "id": "1bd91655",      "name": "Lunch with James",      "amount": 57.5    },    {      "id": "226c3910",      "name": "Flight to Berlin",      "amount": 389.78,      "category": "conferences"    },    {      "id": "db5a4b23",      "name": "Recurring SaaS Subscription",      "amount": 30,      "category": "saas",      "approved_by": "90400852"    }  ]}
```

data.json

```
{  "approvers": {    "7f619663": {      "name": "Alice",      "email": "alice@example.com"    }  }}
```

[Open in OPA Playground](https://play.openpolicyagent.org/?state=eyJpIjoie1xuICBcIml0ZW1zXCI6IFtcbiAgICB7XG4gICAgICBcImlkXCI6IFwiMWJkOTE2NTVcIixcbiAgICAgIFwibmFtZVwiOiBcIkx1bmNoIHdpdGggSmFtZXNcIixcbiAgICAgIFwiYW1vdW50XCI6IDU3LjVcbiAgICB9LFxuICAgIHtcbiAgICAgIFwiaWRcIjogXCIyMjZjMzkxMFwiLFxuICAgICAgXCJuYW1lXCI6IFwiRmxpZ2h0IHRvIEJlcmxpblwiLFxuICAgICAgXCJhbW91bnRcIjogMzg5Ljc4LFxuICAgICAgXCJjYXRlZ29yeVwiOiBcImNvbmZlcmVuY2VzXCJcbiAgICB9LFxuICAgIHtcbiAgICAgIFwiaWRcIjogXCJkYjVhNGIyM1wiLFxuICAgICAgXCJuYW1lXCI6IFwiUmVjdXJyaW5nIFNhYVMgU3Vic2NyaXB0aW9uXCIsXG4gICAgICBcImFtb3VudFwiOiAzMCxcbiAgICAgIFwiY2F0ZWdvcnlcIjogXCJzYWFzXCIsXG4gICAgICBcImFwcHJvdmVkX2J5XCI6IFwiOTA0MDA4NTJcIlxuICAgIH1cbiAgXVxufSIsImQiOiJ7XG4gIFwiYXBwcm92ZXJzXCI6IHtcbiAgICBcIjdmNjE5NjYzXCI6IHtcbiAgICAgIFwibmFtZVwiOiBcIkFsaWNlXCIsXG4gICAgICBcImVtYWlsXCI6IFwiYWxpY2VAZXhhbXBsZS5jb21cIlxuICAgIH1cbiAgfVxufSIsInAiOiJwYWNrYWdlIHBsYXlcblxuaW1wb3J0IHJlZ28udjFcblxuYXBwcm92YWxfbWluIDo9IDM1MFxuXG5yZWFzb25zW2l0ZW0uaWRdIGNvbnRhaW5zIFwiY2F0ZWdvcnkgbXVzdCBiZSBzZXRcIiBpZiB7XG5cdHNvbWUgaXRlbSBpbiBpbnB1dC5pdGVtc1xuXG5cdG9iamVjdC5nZXQoaXRlbSwgXCJjYXRlZ29yeVwiLCBcIlwiKSA9PSBcIlwiXG59XG5cbnJlYXNvbnNbaXRlbS5pZF0gY29udGFpbnMgbWVzc2FnZSBpZiB7XG5cdHNvbWUgaXRlbSBpbiBpbnB1dC5pdGVtc1xuXG5cdGl0ZW0uYW1vdW50ID4gYXBwcm92YWxfbWluXG5cblx0b2JqZWN0LmdldChpdGVtLCBcImFwcHJvdmVkX2J5XCIsIFwiXCIpID09IFwiXCJcblxuXHRtZXNzYWdlIDo9IHNwcmludGYoXG5cdFx0XCJpdGVtcyBvdmVyICVkIG11c3QgYmUgYXBwcm92ZWRcIixcblx0XHRbYXBwcm92YWxfbWluXSxcblx0KVxufVxuXG5yZWFzb25zW2l0ZW0uaWRdIGNvbnRhaW5zIFwiYXBwcm92ZXIgZG9lcyBub3QgZXhpc3RcIiBpZiB7XG5cdHNvbWUgaXRlbSBpbiBpbnB1dC5pdGVtc1xuXG5cdG5vdCBkYXRhLmFwcHJvdmVyc1tpdGVtLmFwcHJvdmVkX2J5XVxufVxuIn0=)