# Context variables

You can use the following context variables within [cube][ref-ref-cubes]
definitions:

- [`CUBE`](#cube) for [referencing members][ref-syntax-references] of the same cube.
- [`FILTER_PARAMS`](#filter_params) and [`FILTER_GROUP`](#filter_group) for optimizing generated SQL queries.
- [`SQL_UTILS`](#sql_utils) for time zone conversion.
- [`COMPILE_CONTEXT`](#compile_context) for creation of [dynamic data models][ref-dynamic-data-models].

## `CUBE`

You can use the `CUBE` context variable to reference columns or members of
the current cube so you don't have to repeat the its name over and over.

It helps [reference members][ref-syntax-references] while keeping the data
model code DRY and easy to maintain.

<CodeTabs>

```javascript
cube(`users`, {
  sql_table: `users`,

  joins: {
    contacts: {
      sql: `${CUBE}.contact_id = ${contacts.id}`,
      relationship: `one_to_one`,
    },
  },

  dimensions: {
    id: {
      sql: `${CUBE}.id`,
      type: `number`,
      primary_key: true,
    },

    name: {
      sql: `COALESCE(${CUBE}.name, ${contacts.name})`,
      type: `string`,
    },
  },
});

cube(`contacts`, {
  sql_table: `contacts`,

  dimensions: {
    id: {
      sql: `${CUBE}.id`,
      type: `number`,
      primary_key: true,
    },

    name: {
      sql: `${CUBE}.name`,
      type: `string`,
    },
  },
});
```

```yaml
cubes:
  - name: users
    sql_table: users

    joins:
      - name: contacts
        sql: "{CUBE}.contact_id = {contacts.id}"
        relationship: one_to_one

    dimensions:
      - name: id
        sql: "{CUBE}.id"
        type: number
        primary_key: true

      - name: name
        sql: "COALESCE({CUBE.name}, {contacts.name})"
        type: string

  - name: contacts
    sql_table: contacts

    dimensions:
      - name: id
        sql: "{CUBE}.id"
        type: number
        primary_key: true

      - name: name
        sql: "{CUBE}.name"
        type: string









```

</CodeTabs>

## `FILTER_PARAMS`

`FILTER_PARAMS` context variable allows you to use [filter][ref-query-filter]
values from the Cube query during SQL generation.

This is useful for hinting your database optimizer to use a specific index
or filter out partitions or shards in your cloud data warehouse so you won't
be billed for scanning those.

<WarningBox>

Heavy usage of `FILTER_PARAMS` is considered a bad practice. It usually
leads to hard-to-maintain data models. Good rule of thumb is to use
`FILTER_PARAMS` only for predicate pushdown performance optimizations.

If you find yourself relying a lot on `FILTER_PARAMS`, it might mean that
you need to rethink your approach to data modeling and potentially move
some transformations upstream. Also, you might reconsider the choice of the
data source.

</WarningBox>

`FILTER_PARAMS` has to be a top-level expression in `WHERE` and it has the
following syntax:

<CodeTabs>

```yaml
cubes:
  - name: cube_name
    sql: >
      SELECT *
      FROM table
      WHERE {FILTER_PARAMS.cube_name.member_name.filter(sql_expression)}

    dimensions:
      - name: member_name
        # ...



```

```javascript
cube(`cube_name`, {
  sql: `
    SELECT *
    FROM table
    WHERE ${FILTER_PARAMS.cube_name.member_name.filter(sql_expression)}
  `,

  dimensions: {
    member_name: {
      // ...
    }
  }
})
```

</CodeTabs>

The `filter()` function accepts `sql_expression`, which could be either
a string or a function returning a string.

### Example with string

See the example below for the case when a string is passed to `filter()`:

<CodeTabs>

```javascript
cube(`order_facts`, {
  sql: `
    SELECT *
    FROM orders
    WHERE ${FILTER_PARAMS.order_facts.date.filter('date')}
  `,

  measures: {
    count: {
      type: `count`,
    },
  },

  dimensions: {
    date: {
      sql: `date`,
      type: `time`,
    },
  },
});
```

```yaml
cubes:
  - name: order_facts
    sql: >
      SELECT *
      FROM orders
      WHERE {FILTER_PARAMS.order_facts.date.filter('date')}

    measures:
      - name: count
        type: count

    dimensions:
      - name: date
        sql: date
        type: time





```

</CodeTabs>

This will generate the following SQL...

```sql
SELECT COUNT(*) AS orders__count
FROM orders
WHERE
  date >= '2018-01-01 00:00:00' AND
  date <= '2018-12-31 23:59:59'
```

...for the `['2018-01-01', '2018-12-31']` date range passed for the
`order_facts.date` dimension as in following query:

```json
{
  "measures": ["order_facts.count"],
  "time_dimensions": [
    {
      "dimension": "order_facts.date",
      "dateRange": ["2018-01-01", "2018-12-31"]
    }
  ]
}
```

### Example with function

You can also pass a function as a `filter()` argument. This way, you can
add BigQuery shard filtering, which will reduce your billing cost.

<CodeTabs>

```yaml
cubes:
  - name: events
    sql: >
      SELECT *
      FROM schema.`events*`
      WHERE {FILTER_PARAMS.events.date.filter(
        lambda x, y: f"""
          _TABLE_SUFFIX >= FORMAT_TIMESTAMP('%Y%m%d', TIMESTAMP({x})) AND
          _TABLE_SUFFIX <= FORMAT_TIMESTAMP('%Y%m%d', TIMESTAMP({y}))
        """
      )}

    dimensions:
      - name: date
        sql: date
        type: time
        


```

```javascript
cube(`events`, {
  sql: `
    SELECT *
    FROM schema.\`events*\`
    WHERE ${FILTER_PARAMS.events.date.filter(
      (x, y) => `
        _TABLE_SUFFIX >= FORMAT_TIMESTAMP('%Y%m%d', TIMESTAMP(${x})) AND
        _TABLE_SUFFIX <= FORMAT_TIMESTAMP('%Y%m%d', TIMESTAMP(${y}))
      `
    )}
  `,

  dimensions: {
    date: {
      sql: `date`,
      type: `time`,
    },
  },
});
```

</CodeTabs>

<InfoBox>

When a function is passed to `filter()`, its arguments are passed as
strings from the data source driver and it's your responsibility to handle
type conversions in this case.

</InfoBox>

In the example above, the filter on a time dimension accepts two values: the
lower and the upper bounds of a date range. If a filter accepts multiple values,
they are passed to the function as individual parameters:

```javascript
cube(`multi_filter`, {
  sql: `
    SELECT 123 AS value
    -- Multiple values: ${FILTER_PARAMS.multi_filter.dummy.filter(
      (...args) => JSON.stringify(args)
    )}
  `,

  dimensions: {
    dummy: {
      sql: `1`,
      type: `number`
    }
  }
})
```

## `FILTER_GROUP`

If you use `FILTER_PARAMS` in your query more than once, you must wrap them
with `FILTER_GROUP`.

<WarningBox>

Otherwise, if you combine `FILTER_PARAMS` with any logical operators other than
`AND` in SQL or if you use filters with [boolean operators][ref-filter-boolean]
in your Cube queries, incorrect SQL might be generated.

</WarningBox>

`FILTER_GROUP` has to be a top-level expression in `WHERE` and it has the
following syntax:

<CodeTabs>

```yaml
cubes:
  - name: cube_name
    sql: >
      SELECT *
      FROM table
      WHERE {FILTER_GROUP(
        FILTER_PARAMS.cube_name.member_name.filter(sql_expression),
        FILTER_PARAMS.cube_name.another_member_name.filter(sql_expression)
      )}

    dimensions:
      - name: member_name
        # ...

      - name: another_member_name
        # ...




```

```javascript
cube(`cube_name`, {
  sql: `
    SELECT *
    FROM table
    WHERE ${FILTER_GROUP(
      FILTER_PARAMS.cube_name.member_name.filter(sql_expression),
      FILTER_PARAMS.cube_name.another_member_name.filter(sql_expression)
    )}
  `,

  dimensions: {
    member_name: {
      // ...
    },
    
    another_member_name: {
      // ...
    }
  }
})
```

</CodeTabs>

### Example

To understand the value of `FILTER_GROUP`, consider the following data model
where two `FILTER_PARAMS` are combined in SQL using the `OR` operator:

<CodeTabs>

```yaml
cubes:
  - name: filter_group
    sql: >
      SELECT *
        FROM (
          SELECT 1 AS a, 3 AS b UNION ALL
          SELECT 2 AS a, 2 AS b UNION ALL
          SELECT 3 AS a, 1 AS b
        ) AS data
        WHERE
          {FILTER_PARAMS.filter_group.a.filter("a")} OR
          {FILTER_PARAMS.filter_group.b.filter("b")}

    dimensions:
      - name: a
        sql: a
        type: number

      - name: b
        sql: b
        type: number




```

```javascript
cube(`filter_group`, {
  sql: `
    SELECT *
      FROM (
        SELECT 1 AS a, 3 AS b UNION ALL
        SELECT 2 AS a, 2 AS b UNION ALL
        SELECT 3 AS a, 1 AS b
      ) AS data
      WHERE
        ${FILTER_PARAMS.filter_group.a.filter('a')} OR
        ${FILTER_PARAMS.filter_group.b.filter('b')}
  `,

  dimensions: {
    a: {
      sql: `a`,
      type: `number`
    },

    b: {
      sql: `b`,
      type: `number`
    }
  }
})
```

</CodeTabs>

If the following query is run...

```json
{
  "dimensions": [
    "filter_group.a",
    "filter_group.b"
  ],
  "filters": [
    {
      "member": "filter_group.a",
      "operator": "gt",
      "values": ["1"]
    },
    {
      "member": "filter_group.b",
      "operator": "gt",
      "values": ["1"]
    }
  ]
}
```

...the following (logically incorrect) SQL will be generated:

```sql
SELECT
  "filter_group".a,
  "filter_group".b
FROM (
  SELECT *
  FROM (
    SELECT 1 AS a, 3 AS b UNION ALL
    SELECT 2 AS a, 2 AS b UNION ALL
    SELECT 3 AS a, 1 AS b
  ) AS data
  WHERE
    (a > 1) OR  -- Incorrect logical operator here
    (b > 1)
) AS "filter_group"
WHERE
  "filter_group".a > 1 AND
  "filter_group".b > 1
GROUP BY 1, 2
```

As you can see, since an array of filters has `AND` semantics, Cube has
correctly used the `AND` operator in the "outer" `WHERE`. At the same time,
the hardcoded `OR` operator has propagated to the "inner" `WHERE`, leading to
a logically incorrect query.

Now, if the cube is defined the following way...

<CodeTabs>

```yaml
cubes:
  - name: filter_group
    sql: >
      SELECT *
        FROM (
          SELECT 1 AS a, 3 AS b UNION ALL
          SELECT 2 AS a, 2 AS b UNION ALL
          SELECT 3 AS a, 1 AS b
        ) AS data
        WHERE
          {FILTER_GROUP(
            FILTER_PARAMS.filter_group.a.filter("a"),
            FILTER_PARAMS.filter_group.b.filter("b")
          )}

    # ...
```

```javascript
cube(`filter_group`, {
  sql: `
    SELECT *
      FROM (
        SELECT 1 AS a, 3 AS b UNION ALL
        SELECT 2 AS a, 2 AS b UNION ALL
        SELECT 3 AS a, 1 AS b
      ) AS data
      WHERE
        ${FILTER_GROUP(
          FILTER_PARAMS.filter_group.a.filter('a'),
          FILTER_PARAMS.filter_group.b.filter('b')
        )}
  `,

  // ...
```

</CodeTabs>

...the following correct SQL will be generated for the same query:

```sql
SELECT
  "filter_group".a,
  "filter_group".b
FROM (
  SELECT *
  FROM (
    SELECT 1 AS a, 3 AS b UNION ALL
    SELECT 2 AS a, 2 AS b UNION ALL
    SELECT 3 AS a, 1 AS b
  ) AS data
  WHERE
    (a > 1) AND  -- Correct logical operator here
    (b > 1)
) AS "filter_group"
WHERE
  "filter_group".a > 1 AND
  "filter_group".b > 1
GROUP BY 1, 2
```

You can also use [boolean operators][ref-filter-boolean] in the Cube query
to express more complex filtering logic:

```json
{
  "dimensions": [
    "filter_group.a",
    "filter_group.b"
  ],
  "filters": [
    {
      "or": [
        {
          "member": "filter_group.a",
          "operator": "gt",
          "values": ["1"]
        },
        {
          "member": "filter_group.b",
          "operator": "gt",
          "values": ["1"]
        }
      ]
    }
  ]
}
```

With `FILTER_GROUP`, the following correct SQL will be generated:

```sql
SELECT
  "filter_group".a,
  "filter_group".b
FROM (
  SELECT *
  FROM (
    SELECT 1 AS a, 3 AS b UNION ALL
    SELECT 2 AS a, 2 AS b UNION ALL
    SELECT 3 AS a, 1 AS b
  ) AS data
  WHERE
    (a > 1) OR
    (b > 1)
) AS "filter_group"
WHERE
  "filter_group".a > 1 OR
  "filter_group".b > 1
GROUP BY 1, 2
```

## `SQL_UTILS`

### `convertTz`

In case you need to convert your timestamp to user request timezone in cube or
member SQL you can use `SQL_UTILS.convertTz()` method. Note that Cube will
automatically convert timezones for `timeDimensions` fields in
[queries](/product/apis-integrations/rest-api/query-format#query-properties).

<WarningBox>

Dimensions that use `SQL_UTILS.convertTz()` should not be used as
`timeDimensions` in queries. Doing so will apply the conversion multiple times
and yield wrong results.

</WarningBox>

In case the same database field needs to be queried in `dimensions` and
`timeDimensions`, create dedicated dimensions in the cube definition for the
respective use:

<CodeTabs>

```javascript
cube(`visitors`, {
  // ...

  dimensions: {
    // Do not use in timeDimensions query property
    created_at_converted: {
      sql: SQL_UTILS.convertTz(`created_at`),
      type: `time`,
    },

    // Use in timeDimensions query property
    created_at: {
      sql: `created_at`,
      type: "time",
    },
  },
});
```

```yaml
cubes:
  - name: visitors
    # ...

    dimensions:
      # Do not use in timeDimensions query property
      - name: created_at_converted
        sql: "{SQL_UTILS.convertTz(`created_at`)}"
        type: time

      # Use in timeDimensions query property
      - name: created_at
        sql: created_at
        type: time



```

</CodeTabs>

## `COMPILE_CONTEXT`

<WarningBox>

`COMPILE_CONTEXT` is evaluated only once per each key generated by `context_to_app_id`.
The `securityContext` defined in `COMPILE_CONTEXT` doesn't change
its value for different users, however, it will change for
different tenants as defined in `context_to_app_id`.

</WarningBox>

A global `COMPILE_CONTEXT` contains `securityContext` and any other variables provided by
[`extendContext`][ref-config-ext-ctx].

Use [Jinja][ref-dynamic-jinja] `{{ }}` syntax to access `COMPILE_CONTEXT` variable.

<CodeTabs>

```yaml
cubes:
  - name: users
    sql_table: "user_{{ COMPILE_CONTEXT.securityContext.deployment_id }}.users"
```

```javascript
cube(`users`, {
  sql_table: `user_${COMPILE_CONTEXT.securityContext.deployment_id}.users`,
});
```

</CodeTabs>

## `SECURITY_CONTEXT`

<WarningBox>

**`SECURITY_CONTEXT` is deprecated and will be removed by the end of 2025**.
Use [`query_rewrite`][ref-config-queryrewrite] instead.

</WarningBox>

`SECURITY_CONTEXT` global variable holds a security context that is passed to Cube via API.
Please read the [Security Context page][ref-sec-ctx] for more information on how
to provide security context to Cube.


```javascript
cube(`orders`, {
  sql: `
    SELECT *
    FROM orders
    WHERE ${SECURITY_CONTEXT.email.filter("email")}
  `,

  dimensions: {
    date: {
      sql: `date`,
      type: `time`,
    },
  },
});
```

To ensure filter value presents for all requests `requiredFilter` can be used:



```javascript
cube(`orders`, {
  sql: `
    SELECT *
    FROM orders
    WHERE ${SECURITY_CONTEXT.email.requiredFilter("email")}
  `,

  dimensions: {
    date: {
      sql: `date`,
      type: `time`,
    },
  },
});
```

You can access values of context variables directly in JavaScript in order to
use it during your SQL generation. For example:

<WarningBox>

Use of this feature entails SQL injection security risk. Use it with caution.

</WarningBox>

```javascript
cube(`orders`, {
  sql: `
    SELECT *
    FROM ${
      SECURITY_CONTEXT.type.unsafeValue() === "employee" ? "employee" : "public"
    }.orders
  `,

  dimensions: {
    date: {
      sql: `date`,
      type: `time`,
    },
  },
});
```

[ref-config-ext-ctx]: /reference/configuration/config#extendcontext
[ref-config-queryrewrite]: /reference/configuration/config#query_rewrite
[ref-config-req-ctx]: /reference/configuration/config#requestcontext
[ref-recipe-control-access-cubes-views]:
  /guides/recipes/access-control/controlling-access-to-cubes-and-views
[ref-sec-ctx]: /product/auth/context
[ref-ref-cubes]: /reference/data-model/cube
[ref-syntax-references]: /product/data-modeling/syntax#references
[ref-dynamic-data-models]: /product/data-modeling/dynamic/jinja
[ref-query-filter]: /product/apis-integrations/rest-api/query-format#query-properties
[ref-dynamic-jinja]: /product/data-modeling/dynamic/jinja
[ref-filter-boolean]: /product/apis-integrations/rest-api/query-format#boolean-logical-operators