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​

PrefixReadsExample
data.fieldNameA named field in the unencrypted message payloaddata.text, data.type, data.user["role"]
meta.fieldNameA named field in the metadata attached with meta at publish timemeta.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​

CategoryOperatorsData typesExample
Equality==, !=String, numbermeta.status == "active", data.score != 0
Ordering>, <, >=, <=Number onlydata.score > 100, meta.count <= 5
PatternLIKE, CONTAINSStringmeta.title LIKE "news*", data.tags CONTAINS "urgent"
Logical&&, ||, !The result of a comparison, pattern, or logical expressionmeta.active == "true" && data.score > 50
Arithmetic+, -, *, /, %Numbermeta.userId % 10 == 0, data.score > (meta.baseline + 10)
Access[n], ["key"]Array element, object property, one level deepmeta.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 typeSyntaxExample
StringAlways quoted: "value"meta.name == "Alice"
NumberUnquoted: 123, 3.14data.score > 85
BooleanCompared as the string "true" or "false", or as a numeric flag such as 1 or 0meta.enabled == "true"
Array elementfield[index], zero-based, one level deepmeta.tags[0] == "urgent"
Object propertyfield["key"], one level deepmeta.user["role"] == "admin"

Expression efficiency​

Relative costExpressionGuidance
LowestDirect field comparison or numeric comparisonPrefer these when they express the rule.
HigherPattern matching or arithmeticUse them only when a direct comparison cannot express the rule.
Depends on order&& expressionPut 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​

FilterablePropertyAccessNotes
YesMessage payloaddata.*Any field in the published message content
YesMessage metadatameta.*Any field attached with meta at publish time
NoPublisher's User IDn/aNot addressable. Include it in meta or the payload, for example as meta.publisher, to filter on it
NoChannel namen/aNot addressable. Include it in meta or the payload to filter on it
NoTimetokenn/aNot addressable
NoCustom or internal message typen/aNot addressable. Include it in meta or the payload to filter on it

Common invalid expressions​

InvalidValidWhy
meta.active == truemeta.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​

Was this page useful?

Last updated on