# Filter with CFL

CFL, the Costfluent Filter Language, writes a cost filter as one line of text:

```sql
(costs.provider = 'Amazon Web Services' AND costs.service = 'Amazon Elastic Compute Cloud') OR costs.region LIKE 'eu-%'
```

It is another way to write the same filter the explorer's clauses build, and it can also combine conditions with OR. Everywhere a cost filter is accepted, CFL is accepted too: the Cost Report explorer, saved reports, virtual tag rules, saved filters, the Public API and the Terraform provider. Costfluent stores every filter as its JSON document and returns it in that form. The only exception is a saved filter, which keeps the text as you wrote it.

## Before you start

- A workspace with at least one connected source, so there are cost values to match.
- Values are case-sensitive and must match the values the explorer lists. In the explorer, add a clause, open its values, and copy one exactly. For example, the provider is `Amazon Web Services`, not `aws`.

## Write a filter in the explorer

1. Open **Cost Reports** and choose **CFL** beside **Add filter**.
2. The **Filter as CFL** box shows the current filter as CFL text. Edit it, or write a new filter.
3. Choose **Apply**. The result updates, and the filter is kept in the page address like any other filter.

If the text does not parse, the box shows why and names the character position, such as `Unknown field 'costs.colour'` ending in `(at position 34)`, and the current filter stays in place. **CFL reference** in the box opens this page.

A filter that uses OR has no clause form, so for such a filter the clauses step aside and the **Filter as CFL** box is the editor. A clause holding several patterns, written as `LIKE ANY (...)`, shows its values read-only, because the clause's single value box would keep only the first.

## Fields

| CFL | Explorer field |
|---|---|
| `costs.provider` | Provider |
| `costs.account_id` | Sub-account |
| `costs.service` | Service |
| `costs.region` | Region |
| `costs.resource_name` | Resource |
| `costs.charge_category` | Charge category |
| `costs.cluster` | Cluster |
| `costs.namespace` | Namespace |
| `tags->>'key'` | Tag, with its tag key |
| `virtual_tags->>'vtag_...'` | Virtual tag, named by its id |

## Operators

| Write | Means |
|---|---|
| `f = 'v'` | is any of `v` |
| `f != 'v'` or `f <> 'v'` | is none of `v` |
| `f IN ('a', 'b')` / `f NOT IN ('a', 'b')` | is any of / is none of |
| `f LIKE '%v%'` / `'v%'` / `'%v'` | contains / starts with / ends with |
| `f NOT LIKE '%v%'` / `'v%'` / `'%v'` | does not contain / does not start with / does not end with |
| `f LIKE 'v'` / `f NOT LIKE 'v'` | is any of / is none of `v` |
| `f LIKE ANY ('a%', 'b%')` | matches at least one pattern |
| `f NOT LIKE ALL ('a%', 'b%')` | matches none of the patterns |
| `f LIKE ALL (...)` / `f NOT LIKE ANY (...)` | one condition per pattern, joined with AND / OR |

Combine conditions with `AND`, `OR` and parentheses, nested as deeply as you need. `AND` binds tighter than `OR`. Keywords and field names are not case-sensitive; values are.

Put values in single quotes and write a quote inside a value as two: `tags->>'owner' = 'o''neil'`. In a `LIKE` pattern, `%` is a wildcard only at the start or the end, `_` is an ordinary character, and `\%` is a literal percent sign.

## Examples

Costs of two services outside one region:

```sql
costs.service IN ('Amazon Elastic Compute Cloud', 'Amazon Simple Storage Service') AND costs.region != 'us-east-1'
```

Production-tagged costs, or anything in a Kubernetes namespace that starts with `payments`:

```sql
tags->>'Environment' = 'production' OR costs.namespace LIKE 'payments%'
```

A filter written in Vantage VQL, such as `costs.provider = 'aws' AND tags.name = 'team' AND tags.value = 'platform'`, is written in CFL as:

```sql
costs.provider = 'Amazon Web Services' AND tags->>'team' = 'platform'
```

## How it behaves

- **OR becomes groups.** Costfluent reads any nesting of AND and OR as up to 10 alternatives, each a list of conditions joined with AND: `a AND (b OR c)` becomes `(a AND b) OR (a AND c)`. A filter holds at most 20 conditions in one alternative and 50 in total. A filter that would pass those limits is refused with a message naming the limit.
- **Limits.** Text is limited to 100,000 characters and to 32 levels of parentheses.
- **Virtual tag rules.** A rule's filter cannot use OR, because rules are already alternatives, tried in order. Write each alternative as its own rule with the same value.
- **Canonical text.** Costfluent shows a filter in one canonical spelling: lowercase field names, uppercase keywords and single spacing. The Public API returns both spellings of any filter from `GET /v1/cost-filters/translate`.
- **No match.** A filter whose values match nothing shows the explorer's usual empty result, not an error. Check the values' spelling and case against the ones the explorer lists.
- **Error messages** are in English.
- **Not supported**, with the error saying so:
  - a missing value, as in `tags.name = NULL`
  - flexible matching with `~*`
  - `costs.allocation`, `costs.marketplace`, `costs.category` and `costs.subcategory`, which have no Costfluent column
  - `costs.provider_account_id`, `costs.resource_id` and `costs.charge_type`: write `costs.account_id`, `costs.resource_name` and `costs.charge_category`
  - `tags.name` and `tags.value`: write `tags->>'key' = 'value'`

## Related

- [Explore cost data](/cost-reports/explore)
- [Virtual tags](/allocation/virtual-tags)
- [Save and manage reports](/cost-reports/manage)
