Azure Web PubSub 服务中的 OData 筛选器语法

Azure Web PubSub 的 filter 参数定义有关将消息发送到连接的包含或排除条件。 此参数在发送到所有人发送到组发送到用户操作中使用。

本文提供了以下资源:

语法

OData 语言中的筛选器是一个布尔表达式。 它可以是多种类型的表达式之一,如 EBNF 说明中所示。

可以使用交互式语法图来探索语法规则。

本文的正式语法部分提供了完整的 EBNF。

标识符

使用 filter 语法可以控制将消息发送到与标识符条件匹配的连接。 Azure Web PubSub 支持以下标识符:

标识符 说明 注意 示例
userId 连接的用户 ID 不区分大小写。 可以在字符串操作中使用它。 userId eq 'user1'
connectionId 连接的连接 ID 不区分大小写。 可以在字符串操作中使用它。 connectionId ne '123'
groups 连接当前所在的组的集合 不区分大小写。 可以在集合操作中使用它。 'group1' in groups

标识符引用连接的属性值。 Azure Web PubSub 支持与连接模型的属性名称匹配的三个标识符。 该服务支持字符串操作中的 userIdconnectionId 标识符,并支持集合操作中的 groups 标识符。

例如,若要筛选出用户 ID 为 user1 的连接,可将筛选器指定为 userId eq 'user1'。 请阅读以下部分学习有关使用筛选器的更多示例。

布尔表达式

筛选器的表达式是一个布尔表达式。 Azure Web PubSub 将消息发送到筛选表达式计算为 true 的连接。

布尔表达式的类型包括:

  • 使用 andornot 运算符合并其他布尔表达式的逻辑表达式。
  • 使用 eqnegtltgele 运算符将字段或范围变量与常量值进行比较的比较表达式。
  • 布尔文本值 truefalse。 当你以编程方式生成筛选器时,这些常量有时很有用。 否则在实践中很少用到它们。
  • 括号中的布尔表达式。 使用括号可以帮助显式确定筛选器中的操作顺序。 本文的运算符优先级部分介绍了 OData 运算符的默认优先级。

支持的操作

filter 语法支持以下操作:

操作员 说明 示例
逻辑运算符
and 逻辑与 length(userId) le 10 and length(userId) gt 3
or 逻辑或 length(userId) gt 10 or length(userId) le 3
not 逻辑非 not endswith(userId, 'milk')
比较运算符
eq Equal userId eq 'user1'
userId eq null
ne 不等于 userId ne 'user1'
userId ne null
gt 大于 length(userId) gt 10
ge 大于或等于 length(userId) ge 10
lt 小于 length(userId) lt 3
le 小于或等于 'group1' in groups
user in ('user1','user2')
In 运算符
in 右操作数必须是括在括号中的基元值的逗号分隔列表,或者是求值为集合的单个表达式 userId ne 'user1'
分组运算符
() 控制表达式的计算顺序 userId eq 'user1' or (not (startswith(userId,'user2'))
字符串函数
string tolower(string p) 获取字符串值的小写形式 tolower(userId) eq 'user1' 可以匹配用户 USER1 的连接
string toupper(string p) 获取字符串值的大写形式 toupper(userId) eq 'USER1' 可以匹配用户 user1 的连接
string trim(string p) 剪裁字符串值 trim(userId) eq 'user1' 可以匹配用户 user1 的连接
string substring(string p, int startIndex)
string substring(string p, int startIndex, int length)
字符串的子字符串 substring(userId,5,2) eq 'ab' 可以匹配用户 user-ab-de 的连接
bool endswith(string p0, string p1) 检查 p0 是否以 p1 结尾 endswith(userId,'de') 可以匹配用户 user-ab-de 的连接
bool startswith(string p0, string p1) 检查 p0 是否以 p1 开头 startswith(userId,'user') 可以匹配用户 user-ab-de 的连接
int indexof(string p0, string p1) 获取 p0p1 的索引,如果 p0 不包含 p1,则返回 -1 indexof(userId,'-ab-') ge 0 可以匹配用户 user-ab-de 的连接
int length(string p) 获取输入字符串的长度 length(userId) gt 1 可以匹配用户 user-ab-de 的连接
集合函数
int length(collection p) 获取集合的长度 length(groups) gt 1 可以匹配两个组中的连接

运算符优先级

如果编写的筛选表达式未将其子表达式括在括号中,Azure Web PubSub 服务将会根据一组运算符优先级规则评估该表达式。 这些规则基于用于合并子表达式的运算符。 下表按从高到低的优先顺序列出了运算符组:

运算符
逻辑运算符 not
比较运算符 eqnegtltgele
逻辑运算符 and
逻辑运算符 or

上表中优先级较高的运算符与其操作数之间的绑定密切性高于其他运算符。 例如,and 的优先级高于 or,而比较运算符的优先级高于上述两者。 因此,下面两个表达式是等效的:

length(userId) gt 0 and length(userId) lt 3 or length(userId) gt 7 and length(userId) lt 10
((length(userId) gt 0) and (length(userId) lt 3)) or ((length(userId) gt 7) and (length(userId) lt 10))

not 运算符的优先级最高。 它甚至高于比较运算符。 如果编写如下所示的筛选器:

not length(userId) gt 5

将出现以下错误消息:

Invalid syntax for 'not length(userId)': Type 'null', expect 'bool'. (Parameter 'filter')

发生此错误的原因是,运算符仅与 length(userId) 表达式相关联,而不与整个比较表达式相关联。 当 userIdnull 时,length(userId) 表达式的类型为 null。 解决方法是将 not 的操作数括在括号中:

not (length(userId) gt 5)

筛选器大小限制

可以发送到 Azure Web PubSub 服务的筛选表达式的大小和复杂性存在限制。 限制大致基于筛选器表达式中的子句数。 一条合理的指导原则是,如果子句超过 100 个,则存在超限的风险。 为避免超出限制,请对应用程序进行设计,使其不会生成无限大小的筛选器。

示例

发送到多个组:

filter='group1' in groups or 'group2' in groups or 'group3' in groups

发送到特定组中的多个用户:

filter=userId in ('user1', 'user2', 'user3') and 'group1' in groups

发送到某个用户而不是特定的连接 ID:

filter=userId eq 'user1' and connectionId ne '123'

发送到不在特定组中的用户:

filter=userId eq 'user1' and (not ('group1' in groups))

当用户 ID 包含 ' 时转义 '

filter=userId eq 'user''1'

正式语法

下面的扩展巴科斯-瑙尔范式 (Extended Backus-Naur Form) 语法可描述 Azure Web PubSub 服务支持的 OData 语言子集。 该语法按“自上而下”的方式列出规则,从最复杂的表达式开始,然后将它们分解为更原始的表达式。 顶部是 $filter 的语法规则,它与 Azure Web PubSub 服务 Send* REST API 的特定 filter 参数相对应。

/* Top-level rule */

filter_expression ::= boolean_expression

/* Identifiers */
string_identifier ::= 'connectionId' | 'userId' 
collection_identifier ::= 'groups'

/* Rules for $filter */

boolean_expression ::= logical_expression
                     | comparison_expression
                     | in_expression
                     | boolean_literal
                     | boolean_function_call
                     | '(' boolean_expression ')'

logical_expression ::= boolean_expression ('and' | 'or') boolean_expression
                     | 'not' boolean_expression

comparison_expression ::= primary_expression comparison_operator primary_expression

in_expression ::= primary_expression 'in'  ( '(' primary_expression (',' primary_expression)* ')' ) | collection_expression  

collection_expression ::= collection_variable
                        | '(' collection_expression ')'

primary_expression ::= primary_variable 
                     | function_call
                     | constant
                     | '(' primary_expression ')'

string_expression ::= string_literal
                    | 'null'
                    | string_identifier
                    | string_function_call
                    | '(' string_expression ')'

primary_variable ::= string_identifier 
collection_variable ::= collection_identifier

comparison_operator ::= 'gt' | 'lt' | 'ge' | 'le' | 'eq' | 'ne'

/* Rules for constants and literals */
constant     ::=      string_literal
                    | integer_literal
                    | boolean_literal
                    | 'null'

boolean_literal ::= 'true' | 'false'

string_literal ::= "'"([^'] | "''")*"'"

digit ::= [0-9]
sign ::= '+' | '-'
integer_literal ::= sign? digit+

boolean_literal ::= 'true' | 'false'

/* Rules for functions */

function_call ::= indexof_function_call 
                | length_function_call 
                | string_function_call
                | boolean_function_call

boolean_function_call ::= endsWith_function_call 
                        | startsWith_function_call 
                        | contains_function_call
string_function_call  ::= tolower_function_call 
                        | toupper_function_call  
                        | trim_function_call 
                        | substring_function_call 
                        | concat_function_call

/* Rules for string functions */
indexof_function_call    ::= "indexof"     '(' string_expression ',' string_expression ')'
concat_function_call     ::= "concat"     '(' string_expression ',' string_expression ')'
contains_function_call   ::= "contains"   '(' string_expression ',' string_expression ')'
endsWith_function_call   ::= "endswith"   '(' string_expression ',' string_expression ')'
startsWith_function_call ::= "startswith" '(' string_expression ',' string_expression ')'
substring_function_call  ::= "substring"  '(' string_expression ',' integer_literal (',' integer_literal)? ')'
tolower_function_call    ::= "tolower"    '(' string_expression ')'
toupper_function_call    ::= "toupper"    '(' string_expression ')'
trim_function_call       ::= "trim"       '(' string_expression ')'

/* Rules for string and collection functions */
length_function_call     ::= "length"     '(' string_expression | collection_expression ')'

后续步骤

使用这些资源开始生成自己的应用程序: