条件过滤和更新语法说明
本文聚焦类 SQL 文本的语法定义:操作符、表达式、数组操作命令。本特性的整体功能、命令支持矩阵、字段路径能力、性能与注意事项见《条件过滤和更新功能》。
1. 版本要求
- SDK 要求版本大于等于 3.55.0,其中 Batch 类操作则要求 3.64.0 以上的版本(目前最高仅提供 3.55.0 的 SDK,即目前批量操作不支持条件更新和过滤查询)。
- Client 工具使用条件过滤要求版本大于等于 3.55.0。
2. 适用范围
主要的增删改查均支持 condition 入参;写类请求额外支持 operation 入参。常见命令包括 Get、Update、Replace、Delete、FieldGet、FieldSet、FieldIncrease、IndexGet、Traverse、ListGet/ListGetAll/ListReplace/ListDelete/ListTraverse、UpdateItem/ListUpdateItem(数组 PUSH/SET/POP)、Query/ListQuery(数组 GET)等。
完整支持矩阵(含各命令最低 SDK 版本、Batch 类与 List 类差异、ListDeleteAll 等历史例外)见《条件过滤和更新功能》§3。
3. 示例
TDR 表示例(C++)
USER u = {};
// 设置主键
u.dwId = 1;
strcpy(u.szName, "a");
record->SetData(&u, sizeof(u));
// 前置的条件过滤,先判断gameids是否已经包含101
record->SetCondition("gameids NOT CONTAINS($ = 101)");
// 后置的数组操作,插入101
record->SetOperation("PUSH gameids#[-1][$ = 101]");
// 发送写请求
// ...
// 处理响应返回值
if (ret == COMMON_ERR_CONDITION_NOT_MATCHED) // 条件不满足,说明gameids已经存在101了
{
// ...
}
PB 表示例(C++)
user u;
// 设置主键
u.set_id(1);
u.set_name("a");
// 设置递增的步长
u.set_rank(1);
// 当rank达到上限100之后,不再对rank递增,否则 +1
std::set<std::string> dottedpaths;
dottedpaths.insert("rank");
ret = api.FieldInc(dottedpaths, &u, "", "rank < 100");
这里不做更多详尽的示例,TcaplusDB 的 C++ SDK、Go SDK 和 Client 均支持条件更新,对于 SDK 的部分接口,其
condition参数填写条件过滤文本,operation参数填写数组操作文本,详情见各 SDK 的 API 说明。
4. 语法能力概览
本节给出
condition和operation文本各自的"长什么样"的全景;具体的形式化语法规则见 §5、§6。
4.1 condition 的常见形态
condition 是一段类 SQL 的 WHERE 表达式:
rank > 1 AND rank < 10 # 比较 + 逻辑
flag & 8 # 位运算
name LIKE 'abc%' # 模糊匹配
level IN (1, 2, 3) # 集合
score BETWEEN 60 AND 100 # 范围
gameids NOT CONTAINS ($ = 101) # 数组包含(标量数组)
mailbox CONTAINS (title = 'tcaplus' AND read = 0) # 数组包含(结构体数组)
size(mailbox) = 0 # 内建函数
$.LastAccessTime < "2024-01-01" # 内建系统字段
pay.order.desc != '' # 嵌套字段
str_map['key'].desc = 'x' # map 元素的嵌套字段
可用元素:
- 比较符:
==/=、!=/<>、<、<=、>、>=。 - 逻辑:
AND、OR、NOT,可使用括号分组。 - 位运算:
&、~、<<、>>。 - 范围/集合/模糊:
BETWEEN ... AND ...、IN (...)、NOT IN (...)、LIKE、NOT LIKE。 - 数组:
array CONTAINS (子条件)、array NOT CONTAINS (子条件),子条件中用$引用当前元素。 - 内建函数(不区分大小写):
size()、length()、substring()、int()/uint()/float()/double()/string()/bytes()。 - 内建系统字段:
$.LastAccessTime、__version__、__ttl__、__index__(List 表)。
4.2 operation 的常见形态
operation 用于写类请求,描述对记录的修改动作:
PUSH gameids #[-1] [$ = 101] # 数组尾插(标量数组)
PUSH mailbox #[-1] [title = 'x', content = 'y'] # 数组尾插(结构体数组)
SET gameids #[1] [$ = 101] # 数组指定下标修改
POP mailbox #[0-10] [title != 'tcaplus'] # 删除满足条件的元素
GET gameids #[0-9] # 数组取段(用于 Query/ListQuery)
PUSH gameids #[0] [$ = 100]; POP gameids #[100] # 多个动作用 ; 串联
# 以下标量字段自增/自减仅 PB 表的 operation 支持,TDR 表请使用 Increase 请求
pay.pay_times += 1 # 标量字段自增(PB)
level -= 1 # 标量字段自减(PB)
可用动作:
- 数组操作:
PUSH/SET/POP/GET,TDR 表与 PB 表均支持。 - 标量字段自增/自减:
field += n、field -= n,支持嵌套字段、数组元素的嵌套字段、map 元素的嵌套字段。仅 PB 表的operation支持;TDR 表请使用专门的Increase请求命令实现字段自增。 - 多个动作之间用
;分隔,TcaplusDB 在一次请求内顺序执行。
5. 条件过滤语法
condition 是类 SQL 的 WHERE 语句的语法,已支持以下几种过滤能力:
- 比较,如
rank > 1,比较符有>、>=、<、<=、=、==、!=、<>,在比较的上下文中,=也是相等比较,而在其他语境下可能是赋值符号。 - 逻辑运算,如
rank > 1 AND rank < 10,运算符有AND、OR、NOT。 - 位运算,仅支持"与",如
filter & 8,当从低往高的第三位为 1 时,该表达式为 true。 CONTAINS和NOT CONTAINS,即判断数组是否包含,如"mailbox CONTAINS(title == \"tcaplus\")"表示要求 mailbox 包含一个 title 等于 "tcaplus" 的元素。CONTAINS 括号中可以是更复杂的子条件。LIKE和NOT LIKE,支持对字符串字段模糊匹配,如"name LIKE 'abc%'",和 MySQL 语法一样,默认忽略大小写,'%'通配任意字符串,'_'匹配任意单字符。IN和NOT IN,判断某字段值是否满足多个散列的值,如"name IN ('abc', 'ABC', '123')"。- 支持嵌套字段,如
mail.title、mailbox[0].title、map_field['key'].value。 - 支持内建函数,常用
size()函数获取数组或者 map 字段的大小,如"size(mailbox) = 0"判断数组是否为空。 - 内建属性
$.LastAccessTime,该内建属性表示记录的最后更新时间,最小精度为秒,可用于和字符串表示的时间进行比较,如"2021"、"2021-01-01"或"2021-01-01 00:00:00"。 - 当前数组元素的引用
$,如"gameids NOT CONTAINS($==101)"。
完整语法如下:
condition_expr ::=
array CONTAINS '(' condition ')'
| array NOT CONTAINS '(' condition ')'
| condition
condition ::=
operand
| operand comparator operand
| condition AND condition
| condition OR condition
| NOT condition
| operand bitwise_op operand
| operand LIKE string
| operand NOT LIKE string
comparator ::=
==
| =
| <
| >
| <=
| >=
| !=
bitwise_op ::=
&
array ::=
identifier
operand ::=
identifier
| number
| string
| $
| $.LastAccessTime
- 语法说明
- identifier: 一个合法的标识名称,在这里是字段名或字段名路径,如
name或mail.title。 - number: 整型或浮点数,不支持大整数。
- string: 双引号或者单引号括起来的字符串,支持类 C 语言风格的转义字符,如
"abc\t123"。
- identifier: 一个合法的标识名称,在这里是字段名或字段名路径,如
- 比较
- 不同精度的整型或浮点型的数值都是可以相互比较的,这和 C++ 语言中是一致的,例如 int16 和 int32 比较,前者的类型会被提升之后再比较,整型和浮点比较,整型则会先被提升为浮点型。
- int 和 uint 可比较,会先比较符号位。
- 浮点型的等值会有精度偏差。
- 字符串也是可比较,按照字母字典序,这和 C++ 中的 std::string 的比较行为是一致的。
- 数值类型和字符串直接不可比较。
- 操作符优先级
- 条件表达式 condition 中,操作符优先级,从高到低为
comparator NOT AND OR,例如"a==1 OR a>10 AND a<20",会先计算 AND 的结果再计算 OR。 - 当然可以使用括号来分隔条件表达式,例如
"(a==1 OR a>10) AND a<20"则就先计算 OR。
- 条件表达式 condition 中,操作符优先级,从高到低为
- 关键字
- 存在一些语法关键字,如
key、like、update等,若存在表的字段名和这些关键字重名,会语法错误,如"key > 100",不过可以使用反引号 ` 把字段名括起来即可规避,如改为"`key` > 100"。 - 因为 TcaplusDB 兼容 MySQL 协议,原则上关键字和 MySQL 的关键字一样,具体可查询:Keywords and Reserved Words。
- 存在一些语法关键字,如
6. Operation 更新
TcaplusDB 支持类 SQL 文本表达的更新操作,支持:
- 数组的 PUSH/POP/SET 操作(TDR 表与 PB 表均支持)。
- 字段自增/自减(仅 PB 表的
operation支持;TDR 表如需字段自增请使用专门的Increase请求命令)。
6.1 自增自减(PB 表)
支持字段、嵌套字段、map 元素嵌套字段、数组元素嵌套字段的自增自减,如:
"pay.pay_times += 1"。"pay_map['kkk'].pay_times += 10"。"pay_array[0].pay_times -= 10"。- 多个操作组合:
"pay.pay_times += 1; PUSH gameids[-1][$=101]"。
TDR 表的
operation文本仅支持 §6.2 的数组操作;提交field += 1这类表达式会被 TcaplusDB 拒绝。
6.2 数组操作语法
TcaplusDB 提供对数组的命令式的操作,具备如下能力:
- PUSH 操作:在数组指定位置插入新的元素数据。
- SET 操作:修改数组指定位置的元素数据。
- POP 操作:删除数组中某些下标范围或者满足某些条件的元素。
- GET 查询:指定记录的 key,查询数组返回数组中某些下标范围或者满足某些条件的元素(即仅记录的局部数据)。
- PUSH/POP/SET 组合操作:一趟请求,可组合多个 PUSH/POP/SET 命令的操作。
TcaplusDB 的 C++ SDK、Go SDK 的部分接口,其 condition 参数是前置的过滤条件,operation 参数是后置的数组操作,详情见各 SDK 的 API 说明。
使用示例:
- 在 mailbox 数组尾部插入一个元素(同时对元素内的 title 等字段赋值),这里 -1 表示尾部的数组下标。
"PUSH mailbox #[-1] [title = 'tcaplus', content = '...']"
- 在 gameids 数组头部插入一个元素 101,这里 0 表示头部的数组下标,$ 表示当前操作的数组元素。
"PUSH gameids #[0] [$ = 101]"
- 删除 mailbox 数组下标 0 ~ 10 范围内,且 title 不等于 "tcaplus" 的元素。
"POP mailbox #[0-10] [title != 'tcaplus']"
- 修改指定下标为 1 的元素。
"SET gameids #[1] [$ = 101]"
- 组合操作,在数组头部插入,同时在尾部删除(删除不存在的元素无影响),保证数组大小总是维持在 100 以内。
"PUSH gameids #[0][$=100]; POP gameids #[100]"
完整的操作语法如下:
push_expr ::=
PUSH array # '[' index ']' '[' assign_expr [, assign_expr]* ']'
set_expr ::=
SET array # '[' index ']' '[' assign_expr [, assign_expr]* ']'
pop_expr ::=
POP array
| POP array # '[' index_range ']'
| POP array # '[' index_range ']' '[' condition ']'
get_expr ::=
GET array
| GET array # '[' index_range ']'
| GET array # '[' index_range ']' '[' condition ']'
assign_expr ::=
identifier = number | string
| $ = number | string
index ::=
integer
index_range ::=
index [, index_range]*
| index - index [, index_range]*
语法说明
- assign_expr: 赋值表达式,若数组元素是组合类型,则是内部字段的赋值
title = 'tcaplus', content = '...',若元素是基本类型,则使用$引用元素本身,如$=101。 - index: 数组的下标,其中 0 表示数组头部下标,在不知道数组大小情况下,使用 -1 表示尾部的下标。
- index_range: 数组下标范围,如
"0 - -1"表示所有的下标,也可以表示多个不连续的范围,如"0 - 8, -1"。 - condition: 嵌套的过滤条件,语法和上一章节记录的条件过滤一致,区别在于,前者的语义上下文是整个记录的数据,这里的是数组元素中的数据。
PUSH/SET 的说明
- 赋值表达式中,当前只支持对整型、浮点、字符串类型的字段赋值。
- 赋值表达式中,不同类型整型、浮点型可以相互赋值,即会进行类型强转,有可能出现截断等情况,类型转换的行为和 C++ 中一致。
- 对于 SET,若数组大小为 N,下标的有效范围是 0 ~ (N - 1) 或 -1。
- 对于 PUSH,若数组大小为 N,下标的有效范围是 0 ~ N 或 -1。
POP 的说明
- POP,删除某些范围或某些满足条件的元素。
- 基于
删除不存在的元素不会报错的原则,POP 指定一个不存在的下标范围时,不会报错,例如数组大小为 10,8-80会删除最后 2 个元素。
7. 性能与注意事项
condition 一般不会带来明显额外开销;TcaplusDB 会对 仅 key/属性字段 的条件、SortList 仅排序字段 的条件等场景做特殊优化,详情见《条件过滤和更新功能》§6。
常见限制:
condition、operation文本最大长度均为 1023 字节。- TDR 表的
SetValue()写入的复杂类型(结构体、数组)不支持条件过滤/数组更新,请改用SetData()。 - TDR 表的数组字段会自动维护 refer 字段(数组实际大小),PUSH/POP 自动同步。
- 字段路径中只允许出现一次
[],不支持map['k'].sub_map['k']等多级容器嵌套。
完整的注意事项与 TDR / PB 表行为差异详情见《条件过滤和更新功能》§2.2 和 §7。