---
source_url: https://www.pubnub.com/docs/pub-sub/subscribe/subscribe-filter-expressions
title: Server-side subscribe filter expressions
updated_at: 2026-09-30T07:20:08.000Z
---

# Server-side subscribe filter expressions

## Documentation index

To discover more PubNub resources:

1. Fetch [PubNub's llms.txt](https://www.pubnub.com/llms-full.txt) for a list of available pages in Markdown format.
2. Identify relevant URLs from that index.
3. Fetch the target pages.

Do not assume a path exists, always check the index first.

A subscribe filter is an expression PubNub evaluates on the server against each message before deciding whether to deliver it to a given client. This syntax reference covers the expression language. A subscribe filter can read only `data.*` and `meta.*`, not other message-envelope fields.

```text
(meta.priority == "high") && (data.score > 80)
```

This expression delivers only messages whose metadata priority is `high` and whose payload score is greater than `80`.

For what a filter is, where it fits among the other ways to narrow a stream, and the design constraints around it, refer to [Filter on the server](https://www.pubnub.com/docs/pub-sub/subscribe/overview.md#filter-on-the-server). For the calls that set an expression on a client, refer to [Filter received messages](https://www.pubnub.com/docs/pub-sub/subscribe/filter-received-messages.md).

## Data sources

| Prefix | Reads | Example |
| --- | --- | --- |
| `data.fieldName` | A named field in the unencrypted message payload | `data.text`, `data.type`, `data.user["role"]` |
| `meta.fieldName` | A named field in the metadata attached with [meta](https://www.pubnub.com/docs/pub-sub/publish/send-different-message-types.md#attach-metadata-to-a-message) at publish time | `meta.priority`, `meta.region`, `meta.user["role"]` |

You must reference a specific named field. A bare wildcard such as `data.*` is not a valid expression and cannot be used in a comparison.

When message encryption is enabled, a subscribe filter cannot read `data.*`. `meta.*` remains unencrypted and filterable, so never put a secret in `meta`.

## Operators

| Category | Operators | Data types | Example |
| --- | --- | --- | --- |
| Equality | `==`, `!=` | String, number | `meta.status == "active"`, `data.score != 0` |
| Ordering | `>`, `<`, `>=`, `<=` | Number only | `data.score > 100`, `meta.count <= 5` |
| Pattern | `LIKE`, `CONTAINS` | String | `meta.title LIKE "news*"`, `data.tags CONTAINS "urgent"` |
| Logical | `&&`, `||`, `!` | The result of a comparison, pattern, or logical expression | `meta.active == "true" && data.score > 50` |
| Arithmetic | `+`, `-`, `*`, `/`, `%` | Number | `meta.userId % 10 == 0`, `data.score > (meta.baseline + 10)` |
| Access | `[n]`, `["key"]` | Array element, object property, one level deep | `meta.tags[0]`, `meta.user["role"]` |

## Operator precedence

An expression evaluates in this order, from tightest-binding to loosest:

1. Parentheses: `()`
2. Unary logical NOT: `!`
3. Multiplicative arithmetic: `*`, `/`, `%`
4. Additive arithmetic: `+`, `-`
5. Comparison and pattern: `==`, `!=`, `<`, `>`, `<=`, `>=`, `LIKE`, `CONTAINS`
6. Logical AND: `&&`
7. Logical OR: `||`

Parenthesize a compound expression instead of relying on this order. `((meta.priority == "high") || (meta.priority == "critical")) && (data.score > 80)` states the intended grouping directly, so it stays correct if a clause is added later.

## Data types

| Data type | Syntax | Example |
| --- | --- | --- |
| String | Always quoted: `"value"` | `meta.name == "Alice"` |
| Number | Unquoted: `123`, `3.14` | `data.score > 85` |
| Boolean | Compared as the string `"true"` or `"false"`, or as a numeric flag such as `1` or `0` | `meta.enabled == "true"` |
| Array element | `field[index]`, zero-based, one level deep | `meta.tags[0] == "urgent"` |
| Object property | `field["key"]`, one level deep | `meta.user["role"] == "admin"` |

## Expression efficiency

| Relative cost | Expression | Guidance |
| --- | --- | --- |
| Lowest | Direct field comparison or numeric comparison | Prefer these when they express the rule. |
| Higher | Pattern matching or arithmetic | Use them only when a direct comparison cannot express the rule. |
| Depends on order | `&&` expression | Put the most selective condition first. |

## Pattern matching: LIKE and CONTAINS

`LIKE` matches a string against a wildcard pattern. `*` stands for zero or more characters and can appear at the start, the end, or both:

```text
meta.category LIKE "news*"       // starts with "news"
data.title LIKE "*breaking*"     // contains "breaking"
meta.version LIKE "2.1*"         // starts with "2.1"
```

Matching is case-insensitive. A backslash escapes a literal asterisk, as in `"literal\*"`.

`CONTAINS` matches a substring, with no wildcard syntax:

```text
meta.tags CONTAINS "urgent"
data.description CONTAINS "error"
```

## Array and object access

`field[index]` reads an array element, and `field["key"]` reads an object property, on either `data.*` or `meta.*`. Access is one level deep. `meta.tags[0]` and `meta.user["role"]` are valid. Chaining a second level, such as `meta.config["alerts"]["email"]` or `meta.data["users"][0]`, is not.

```text
meta.tags[0] == "urgent"                 // array element
data.recipients[1] LIKE "*@company.com"  // array element, pattern match
meta.user["role"] == "admin"             // object property
data.config["enabled"] == "true"         // object property
```

## Boolean comparison

A filter expression sees a JSON boolean in the payload or metadata as a string, not as the literal `true` or `false`. Compare it as a string, or use a numeric flag such as `1` or `0` instead:

```text
meta.enabled == "true"
meta.active != "false"
meta.isActive == 1
```

## Filterable and non-filterable properties

| Filterable | Property | Access | Notes |
| --- | --- | --- | --- |
| Yes | Message payload | `data.*` | Any field in the published message content |
| Yes | Message metadata | `meta.*` | Any field attached with `meta` at publish time |
| No | Publisher's User ID | n/a | Not addressable. Include it in `meta` or the payload, for example as `meta.publisher`, to filter on it |
| No | Channel name | n/a | Not addressable. Include it in `meta` or the payload to filter on it |
| No | Timetoken | n/a | Not addressable |
| No | Custom or internal message type | n/a | Not addressable. Include it in `meta` or the payload to filter on it |

## Common invalid expressions

| Invalid | Valid | Why |
| --- | --- | --- |
| `meta.active == true` | `meta.active == "true"` | Boolean values compare as strings, not literals |
| `meta.priority = "high"` | `meta.priority == "high"` | `=` is assignment, not comparison. Use `==` |
| `meta.priority > "low"` | Use a numeric field instead | `>`, `<`, `>=`, `<=` compare numbers only. They do not apply to strings |
| `publisher == "alice"` | `meta.publisher == "alice"` | Envelope fields such as the publisher's User ID aren't addressable. Include the value in `meta` or the payload at publish time |
| `meta.data["users"][0] == "alice"` | `meta.users[0] == "alice"` | Array and object access is one level deep. Chained access isn't supported |

:::note Legacy behavior: bare field names
A filter that omits a prefix, such as `publisher == "alice"`, currently resolves as `meta.publisher == "alice"` on the server instead of returning an error. This behavior exists for backward compatibility and may not be preserved. Always use the explicit `meta.fieldName` or `data.fieldName` form.
:::

## Next steps

* [Filter received messages](https://www.pubnub.com/docs/pub-sub/subscribe/filter-received-messages.md). Set a filter expression on a client and make a value filterable.
* [Filter on the server](https://www.pubnub.com/docs/pub-sub/subscribe/overview.md#filter-on-the-server). What a subscribe filter is, where it runs, and its design constraints.
* [Subscribe](https://www.pubnub.com/docs/pub-sub/subscribe/overview.md). The subscribe model a filter narrows.
* [Send different message types](https://www.pubnub.com/docs/pub-sub/publish/send-different-message-types.md). Attach the `meta` a filter reads.

Last updated at: 2026-09-30T07:20:08.000Z
