App Context filtering
Starting a new app? Use DataSync
DataSync is the successor to App Context. It does everything App Context does for users, channels, and memberships, and adds typed schemas, partial updates with ETags, field-level access control, and per-class expiry. Refer to How DataSync compares to App Context for a feature-by-feature comparison.
DataSync is currently available to new accounts and to accounts that are not actively using App Context. If your keysets already use App Context, keep using it for now. App Context remains fully supported and these pages stay accurate.
An App Context filter expression is the string a filter parameter accepts on an App Context call, so PubNub returns only the records that match instead of every one. This reference covers which fields you can filter for each entity, every operator, and the exact syntax. Filtering requires App Context enabled on your keyset.
name LIKE "John*" && status == "active"
This expression matches only records whose name starts with John and whose status is active.
Filter without writing code
BizOps Workspace applies this same filter language through a UI, no SDK required. In the Admin Portal, use BizOps Workspace → User Management to filter users and their channel memberships. Use BizOps Workspace → Channel Management to filter channels and their user members. Each exposes the fields and operators below through a Filters button instead of a filter parameter.
This is a different language from subscribe filter expressions, which filter messages on the server before delivery and support different operators such as CONTAINS and arithmetic. Don't mix the two.
Where filter applies
Which calls accept filter depends on the entity:
| Entity | Calls that accept filter |
|---|---|
| User metadata | The paginated "get all" call only |
| Channel metadata | The paginated "get all" call only |
| Membership (user → channels) | getMemberships, setMemberships, removeMemberships, and manageMemberships |
| Members (channel → users) | getChannelMembers, setChannelMembers, removeChannelMembers, and manageChannelMembers |
A single user or channel metadata record has no need for a filter, so get, set, and remove on one user or one channel don't accept it. Membership and member calls all return a list, even a write call such as setChannelMembers, so all four accept filter there. On a write call, the filter narrows which of the affected records come back in the response. It doesn't change which records get written or removed.
Fields you can filter
User fields
| Field | Description |
|---|---|
id | The User ID |
name | The user's display name |
externalId | An identifier linking the user to an external system |
profileUrl | URL to the user's profile picture |
email | The user's email address |
status | The condition the user is in, such as active |
type | A category used to classify the user, such as SupportAgent |
updated | Timestamp of the last update to the user's metadata |
custom | Any field inside the user's custom object |
Channel fields
| Field | Description |
|---|---|
id | The channel name |
name | The channel's display name |
description | The channel's description |
status | The condition the channel is in, such as archived |
type | A category used to classify the channel, such as OffTopic |
updated | Timestamp of the last update to the channel's metadata |
custom | Any field inside the channel's custom object |
Not every SDK exposes status and type as filterable on user and channel metadata. Check your platform's API reference in Available SDKs before relying on either.
Membership and member fields
A membership call and a members call read the same underlying record from opposite directions. Each accepts a different field prefix for the side it isn't querying by ID:
| Call direction | Prefix for the other side | Common fields (no prefix) |
|---|---|---|
Memberships (getMemberships, setMemberships, and so on) | channel.* | status, type, custom |
Members (getChannelMembers, setChannelMembers, and so on) | uuid.* | status, type, custom |
A memberships call rejects uuid.* fields, and a members call rejects channel.* fields. type on the common fields is only filterable through the REST API, not every SDK. The uuid prefix refers to the same entity as a User ID. Refer to Why type says uuid for why the field carries that name.
channel.* field (memberships only) | uuid.* field (members only) |
|---|---|
channel.id | uuid.id |
channel.name | uuid.name |
channel.description | uuid.externalId |
channel.status | uuid.profileUrl |
channel.type | uuid.email |
channel.updated | uuid.status |
channel.custom | uuid.type |
uuid.updated | |
uuid.custom |
Filtering through a custom field, on any entity, isn't recommended. Refer to Prefer exact matches over pattern and custom-field filters.
Operators
| Category | Operators | Example |
|---|---|---|
| Comparison | ==, !=, <, >, <=, >= | status == "active", updated >= "2024-01-01T00:00:00Z" |
| Logical | &&, ||, unary ! | status == "active" && type == "member", !(status == "inactive") |
| Pattern | LIKE | name LIKE "John*" |
Group a compound expression with parentheses instead of relying on evaluation order, so it reads correctly and stays correct if a clause is added later:
(channel.type == "private" || channel.type == "restricted") && status == "active"
Pattern matching with LIKE
LIKE matches a string against a wildcard pattern, where * stands for zero or more characters and can appear at the start, the end, or both:
name LIKE "John*" // starts with "John"
name LIKE "*Smith" // ends with "Smith"
description LIKE "*support*" // contains "support"
Escape a literal * in the pattern with a backslash:
name LIKE "*\**" // matches a name that contains a literal asterisk
Data types
| Type | Syntax | Example |
|---|---|---|
| String | Enclosed in double quotes | name == "Alice" |
| Number | Unquoted integer, decimal, or scientific notation, inside a custom field | custom.score > 100 |
| Boolean | true or false, inside a custom field | custom.public == true |
| Null | null | description == null |
| Timestamp | An ISO 8601 string, compared like any other string | updated >= "2024-01-01T00:00:00Z" |
Escape a double quote inside a string value with a backslash:
description == "Say \"hi\" to the team"
When you write the expression inside your own language's string literal, you may need a second layer of escaping for that language's own quote character. Check your SDK's API reference in Available SDKs for its convention.
Null and custom fields
An object that has no referenced custom property is excluded from a comparison, regardless of the operator. Use null when you need to test whether a property exists:
custom.label == null // matches records without custom.label
custom.label != null // matches records with custom.label
The value in a custom comparison must have the same type as the stored property. For example, if custom.score is a number, compare it to 100, not "100". Comparing different types is an error.
Identifiers and property access
| Form | Use it for | Example |
|---|---|---|
| Identifier | A field name that starts with a letter, $, or _, followed by letters, digits, $, or _ | name, $userID, _internal_flag |
| Bracket notation | A field name with characters an identifier can't contain, such as a hyphen or a space | custom["employment-status"] == "valid" |
| Property path | A nested field on a membership or member, using dot notation | channel.custom.team == "alpha", uuid.custom.role == "moderator" |
custom.employment-status == "valid" is invalid. The hyphen isn't allowed in an identifier. Use custom["employment-status"] instead.
REST query encoding
When you call the REST API directly, URL-encode the entire filter expression in the query string. For example, encode custom.public == true as custom.public%20%3D%3D%20true. SDKs take the unencoded filter expression as a method parameter.
Prefer exact matches over pattern and custom-field filters
For an application with many users, channels, or memberships, filter on exact ID equality, such as id == "channel-123", rather than on LIKE pattern matching or a custom field. An exact-match filter on id uses a database index, while a pattern match or a custom field lookup scans every record. This is why filtering through custom isn't recommended for any entity.
The same rule applies in BizOps Workspace. The quick search box runs a contains search across every record. The Filters button with an equals condition on id uses the index instead, so prefer it for a large number of records.
Use a filter in an SDK call
Every SDK's method signature takes the same filter string. Only the parameter name and call shape differ. The following filters both a user list and a channel list by name, using the PHP SDK's filter() method:
1
Check the API reference for your platform in Available SDKs for the exact parameter name and call shape.
Next steps
- App Context. The three entity types, how to turn on App Context, and where filtering fits among the other operations.
- Get metadata for all users. Page through user metadata and apply a filter.
- Get metadata for all channels. Page through channel metadata and apply a filter.
- Set, get, and remove memberships. Filter a user's channel list.
- Set, get, and remove members. Filter a channel's user roster.
- User Management. Filter users and memberships without code.
- Channel Management. Filter channels and members without code.
- Subscribe filter expressions. The unrelated server-side language that filters messages before delivery.