Server-side subscribe filter expressions
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.
(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. For the calls that set an expression on a client, refer to Filter received messages.
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 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:
- Parentheses:
() - Unary logical NOT:
! - Multiplicative arithmetic:
*,/,% - Additive arithmetic:
+,- - Comparison and pattern:
==,!=,<,>,<=,>=,LIKE,CONTAINS - Logical AND:
&& - 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:
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:
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.
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:
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 |
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. Set a filter expression on a client and make a value filterable.
- Filter on the server. What a subscribe filter is, where it runs, and its design constraints.
- Subscribe. The subscribe model a filter narrows.
- Send different message types. Attach the
metaa filter reads.