条件过滤和更新功能
条件过滤和更新包含条件过滤(
condition入参)和更新操作(operation入参)两部分。本文从功能和能力的角度梳理 TcaplusDB 在这一特性上支持的范围、表行为差异、性能优化以及使用注意事项;类 SQL 文本的语法定义见《条件过滤和更新语法说明》。
1. 概述
condition 与 operation 由两个独立的字符串参数承载,由 TcaplusDB 存储层统一解析和执行。一个写请求可同时携带前置过滤(condition)与后置更新(operation):
- 仅当
condition在记录的当前数据上成立时,更新或返回操作才会执行;否则请求返回COMMON_ERR_CONDITION_NOT_MATCHED。 - 读类请求只携带
condition,用于过滤将要返回给客户端的数据。
文档分工:
- 《05条件过滤和更新语法说明》:类 SQL 的文本语法定义、操作符表、示例片段。
- 本文(06):从能力维度回答 支持哪些命令、字段路径、运算符、内建函数、内建系统字段;TDR 与 PB 的差异;性能特征;常见限制与注意事项。
- 《02PB表SDK_and_API/部分字段查询和更新》:与 PB 的
FieldGet/FieldSet/FieldUpdate接口配合使用时的 字段路径集合 用法。
2. 功能概览
本节以"概览 + 差异 + 典型场景"的方式让你快速判断本特性是否能覆盖你的需求。详细形态见 §4 / §5。
2.1 主要能力清单
条件过滤(condition):
- 比较:
==、!=、<、<=、>、>=,跨整型/浮点精度比较;字符串按字典序比较。 - 逻辑:
AND/OR/NOT,可加括号控制优先级。 - 集合 / 范围:
IN、NOT IN、BETWEEN ... AND ...。 - 字符串模糊匹配:
LIKE/NOT LIKE,与 MySQL 一致,默认忽略大小写。 - 数组包含:
array CONTAINS (子条件)/NOT CONTAINS,子条件中用$引用元素。 - 位运算:
&、~、<<、>>,常用于 flag 判断。 - 内建函数:
size()、length()、substring()、类型转换(int/uint/float/double/string/bytes)。 - 内建系统字段:
$.LastAccessTime、__version__、__ttl__、List 表的__index__。 - 字段路径:支持嵌套字段、map 元素、数组元素及其嵌套字段。
更新操作(operation):
- 数组的 PUSH / POP / SET / GET 命令式操作(TDR 表与 PB 表均支持)。
- 标量字段自增/自减:
pay.pay_times += 1、level -= 1(仅 PB 表的operation支持;TDR 表请使用专门的Increase请求命令实现字段自增)。 - PB 表 map 元素的 PUSH / POP(按 key 插入或删除)。
- 多个操作用分号
;串联,一次请求内顺序执行。
2.2 TDR 表 vs PB 表的关键差异
| TDR 表 | PB 表 | |
|---|---|---|
| Map 类型 | 不支持(TDR 协议无 map) | 支持 map 元素的过滤、PUSH/POP,以及 schema-free 用法 |
数组的 refer 字段 |
自动维护:PUSH/POP 会自动改写 refer | 不适用 |
SetValue 与复杂类型 |
SetValue() 写入的结构体/数组不支持条件过滤和更新;请用 SetData() |
不适用 |
数组元素的嵌套字段(pay_array[0].desc) |
condition 中支持;更新只能通过 PUSH/SET/POP |
condition 中支持;字段集合更新(FieldSet)不支持,需借助 PUSH/SET/POP |
operation 中的标量字段 += / -= |
不支持;如需字段自增请使用专门的 Increase 请求命令 |
支持 |
| 自增命令 | Increase 在 3.64.0+ 才支持 condition;低版本可改用 Update |
FieldInc/FieldIncrease 在 3.55.0+ 即支持 |
| Schema-free 用法 | 受限 | FieldSet + map 字段可实现,详见 Schema_Free |
2.3 典型使用场景
| 场景 | 推荐写法 |
|---|---|
| 已存在则不重复加好友 | condition = "friends NOT CONTAINS ($ = ?)" + operation = "PUSH friends #[-1] [$ = ?]" |
| 限制等级上限的自增(PB 表) | condition = "rank < 100" + operation = "rank += 1";TDR 表请改用 Increase 请求 |
| 维护固定大小数组(头插尾删) | operation = "PUSH gameids #[0] [$ = ?]; POP gameids #[100]" |
| 仅读最近未变化的记录 | condition = "$.LastAccessTime < \"2024-01-01\"" |
| 删除过期邮件 | operation = "POP mailbox #[0--1] [expire_time < ?]" |
| 标志位过滤 | condition = "flag & 8" |
3. 支持条件过滤和更新的命令
下表汇总各类请求是否支持 condition/operation,以及对应 SDK 的最低版本要求。命令名以 PB SDK 为主,括号内是 TDR/底层等价名。
| 类别 | 命令 | condition |
operation |
备注 |
|---|---|---|---|---|
| 单记录读 | Get | 是 | - | 3.55.0+ |
| 单记录读 | FieldGet (PB) | 是 | - | 3.55.0+;与字段路径集合配合 |
| 单记录写 | Insert | - | - | 不参与条件流程 |
| 单记录写 | Update | 是 | 是 | 3.55.0+ |
| 单记录写 | Replace | 是 | 是 | 3.55.0+ |
| 单记录写 | Delete | 是 | - | 3.55.0+ |
| 单记录写 | FieldSet / FieldUpdate (PB) | 是 | 是 | 3.55.0+;与字段路径集合配合 |
| 单记录写 | FieldInc / FieldIncrease (PB) | 是 | 是 | 3.55.0+ |
| 单记录写 | Increase (TDR) | 是 | 是 | 3.64.0+;低版本可改用 Update 实现自增 |
| 索引 / 部分键 | IndexGet / GetByPartKey | 是 | - | 3.55.0+ |
| 索引 / 部分键 | IndexDelete (PB) / DeletePartKey (TDR) | 是 | - | 3.64.0+ |
| 索引 / 部分键 | UpdateByPartKey (TDR) | 是 | 是 | 3.64.0+ |
| 全表遍历 | Traverse / ListTraverse | 是 | - | 3.55.0+ |
| 数组 (TDR) | UpdateItem (PUSH/SET/POP) | 是 | 是 | 3.55.0+ |
| 数组 (TDR) | Query (GET) | 是 | 是 | 3.55.0+ |
| List 表 | ListGet / ListGetAll | 是 | - | 3.55.0+ |
| List 表 | ListReplace | 是 | 是 | 3.55.0+ |
| List 表 | ListDelete | 是 | - | 3.55.0+ |
| List 表 | ListDeleteAll | 否 | - | 历史原因暂不支持 |
| List 表 | ListUpdateItem | 是 | 是 | 3.55.0+ |
| List 表 | ListQuery | 是 | 是 | 3.55.0+ |
| Batch 读 | BatchGet / BatchGetByPartKey / BatchFieldGet / ListGetBatch | 是 | - | 3.64.0+ |
| Batch 写 | BatchUpdate / BatchReplace / BatchDelete / ListDeleteBatch / ListUpdateBatch / ListReplaceBatch | 是 | 是 | 3.64.0+ |
表中"3.64.0+"指 SDK 与 TcapSvr 版本同时达标。当前公开 SDK 最高为 3.55.0,因此 Batch 类的条件过滤/更新及 TDR 的
Increase、UpdateByPartKey、DeletePartKey的条件能力暂未对外开放,确有需求可联系 Tcaplus。
4. 条件过滤功能详解
4.1 支持的运算符与内建函数
condition 是类 SQL 的 WHERE 表达式,TcaplusDB 解析为统一的表达式树后执行。
- 比较:
==/=、!=/<>、<、<=、>、>=。整型与浮点可跨精度比较,规则与 C++ 一致;字符串按字典序比较;数值与字符串之间不可比较。 - 逻辑:
AND、OR、NOT,可使用括号控制优先级。 - 位运算:
&(与)、~(取反)、<</>>(移位)。常用于 flag 字段判断,如filter & 8。 - 范围:
BETWEEN ... AND ...。 - 集合:
IN (v1, v2, ...)、NOT IN (...)。 - 模糊匹配:
LIKE、NOT LIKE,仅作用于字符串字段;与 MySQL 一致,默认忽略大小写,%匹配任意字符串、_匹配单字符。 - 数组包含:
array CONTAINS (子条件)、array NOT CONTAINS (子条件),括号中的子条件可使用$引用当前数组元素,详见 §4.4。 - 内建函数(函数名不区分大小写):
- 类型转换:
int(x)、uint(x)、float(x)、double(x)、string(x)、bytes(x)。 - 长度类:
length(s)(字符串字节长度)、size(arr|map)(数组或 map 元素个数,可用于size(mailbox) = 0判断空数组/空 map)。 - 字符串:
substring(s, begin[, length])。
- 类型转换:
4.2 字段路径
字段路径在 condition 中作为标识使用,TcaplusDB 解析后会按照表的元信息逐级解析。下面列出的路径形态在 TDR 表与 PB 表中均支持(差异之处会单独说明)。下面以一个 PB 表为例说明(表结构与《部分字段查询和更新》一致),TDR 表把对应字段换成 TDR 表定义即可:
message tb_online3 {
int32 openid = 1;
string timekey = 3;
pay_info pay = 5; // 嵌套 message
repeated pay_info pay_array = 6; // 数组
repeated string desc_array = 7;
map<int32, all_type_t> int_map = 9;
map<string, all_type_t> str_map = 10;
}
支持的路径形态:
- 一级字段:
timekey、openid。 - 嵌套字段(任意层级):
pay.order.desc、pay.pay_times,例如 TDR 表中的single_struct.sub_field。 - 数组元素:
pay_array[0]、desc_array[3],下标-1表示尾部。TDR 表的标量数组同样支持,例如items[-1]。 - 数组元素的嵌套字段:
pay_array[0].desc、pay_array[0].order.desc,TDR 结构体数组同样支持,例如struct_array[0].title、single_struct.sub_array[0].x这样路径中只含一个[]的形态。可使用pay_array[0-100]这样的范围(仅在Query/GET等数组取值场景使用,普通condition中不接受范围,请用单下标或CONTAINS)。 - Map 元素:
int_map[1001]、str_map['key']、str_str['key'](仅 PB 表,TDR 协议无 map)。 - Map 元素的嵌套字段:
str_map['key'].desc(仅 PB 表)。
不支持或需注意的形态:
- 路径中不能出现两次容器下标:如
map['key'].a.sub_map['sub_key']、pay_array[0].sub_array[1]、a.b[1].c[2]均不支持;同理嵌套数组(数组的元素又是数组)也不支持。 - 普通
condition中的数组下标只接受单下标(如items[0]),不接受items[0-2]这种范围;范围仅用于Query/GET等专门的数组取值场景。 - TDR 表的数组元素本身不可再为数组(TDR 协议本身限制)。
- 字段名若与 SQL 关键字(如
key、like、update)冲突,需要使用反引号转义:`key` > 100。关键字集合参考 MySQL 5.7 Keywords。
4.3 内建系统字段
下面这些字段不属于表定义,但可以在 condition 中直接引用,TcaplusDB 会从记录的元信息(属性头部)中读取:
$.LastAccessTime(等价__last_access_time__):记录最后访问时间,秒级精度,可与字符串形式时间比较,如$.LastAccessTime < "2024-01-01"。__version__:记录版本号。__ttl__:记录的 TTL(秒)。__index__(List 表,等价-index):当前 List 元素的 index 值。
这些字段的好处是仅依赖记录的属性头部信息,参与过滤时通常不需要解包记录的 value 字段,性能更优(详见 §6)。
4.4 数组的 CONTAINS / NOT CONTAINS 与 $ 引用
array CONTAINS (子条件) 检查数组中是否存在满足子条件的元素:
- 标量数组:用
$引用当前元素本身。例如gameids CONTAINS ($ = 101)、gameids NOT CONTAINS ($ = 101)。 - 结构体数组:直接使用元素的字段名。例如
mailbox CONTAINS (title = "tcaplus" AND read = 0)。 - 子条件可以是任意复杂的
AND/OR/NOT/IN/... 组合。
4.5 LIKE / IN / BETWEEN 的常见用法
name LIKE 'abc%'匹配 abc 开头;name LIKE '_bc'匹配长度为 3 且后两字符为 bc。name IN ('abc', 'ABC', '123')、level NOT IN (1, 2, 3)。score BETWEEN 60 AND 100等价于score >= 60 AND score <= 100。
5. 更新(Operation)功能详解
operation 与 condition 同为类 SQL 文本,在写类请求中描述对记录的修改动作;多个动作之间可通过分号 ; 串联,TcaplusDB 在一次请求内顺序执行。
5.1 标量字段的自增/自减
仅 PB 表的
operation支持这种用法。TDR 表的operation文本只支持 §5.2 的数组 PUSH/POP/SET/GET 操作,提交field += 1这类表达式会被 TcaplusDB 拒绝;TDR 表如需字段自增,请使用专门的Increase请求命令(不通过operation文本,而是请求体上独立携带要自增的字段和步长)。
PB 表支持对标量字段或嵌套标量字段使用 +=、-= 进行原子自增/自减:
"pay.pay_times += 1"
"pay_map['kkk'].pay_times += 10"
"pay_array[0].pay_times -= 10"
仅 +=、-= 会被识别为字段自增更新;其它 *=、/=、&=、|= 在 operation 场景下不会作用到字段上。
5.2 数组的 PUSH / POP / SET / GET
针对 repeated 字段(PB)和数组字段(TDR),支持四种命令式操作:
- PUSH:向指定下标插入元素。下标
-1表示尾部。- 标量数组:
PUSH gameids #[-1] [$ = 101]。 - 结构体数组:
PUSH mailbox #[-1] [title = 'tcaplus', content = '...']。
- 标量数组:
- SET:覆盖指定下标的元素,下标必须已存在。
SET gameids #[1] [$ = 101]。 - POP:删除元素,可指定下标范围或附加条件;删除不存在的下标不报错。
POP mailbox #[0-10] [title != 'tcaplus']。POP gameids #[100]:删除第 100 个,超过数组大小时无影响。
- GET:返回数组某段(仅返回,不修改),常用于
Query/ListQuery等数组查询请求。GET gameids #[0-9]。
赋值类型:标量数组用 $ = 值;结构体数组用字段名 [a = ..., b = ...]。不同精度整型/浮点之间会按 C++ 强转规则转换,可能截断。
5.3 多条操作的组合
多个操作之间用分号 ; 连接,TcaplusDB 在一次请求内顺序执行:
"pay.pay_times += 1; PUSH gameids #[-1] [$ = 101]"
"PUSH gameids #[0] [$ = 100]; POP gameids #[100]" // 头插尾删,保持数组长度 ≤ 100
5.4 与 PB 的 FieldSet / FieldUpdate 协同
PB 的 FieldSet(C++)/FieldUpdate(Go)以"字段路径集合 + message 增量数据 + operation + condition"四个入参一次完成请求:
- 字段路径集合用于声明本次更新涉及的字段,message 中只填入这些字段的目标值。
operation可携带数组操作或自增动作,作为字段集合更新之外的额外操作。- 涉及的字段路径集合本身的能力(嵌套字段、map 元素的插入/删除等)属于 字段更新,详见《部分字段查询和更新》。
6. 性能特征与优化
condition 通常不会带来明显额外开销:存储后端的瓶颈在 IO,CPU 上多一次条件判断对吞吐基本无感知。例如 GetByPartKey 扫描 100 条数据,无 condition 与有 condition 仅在 返回数据量 上有差异。
TcaplusDB 会根据 condition 表达式的特征选择不同的执行路径:
- 仅 key 字段或属性字段:当条件只用到主键、索引字段或
$.LastAccessTime、__version__、__ttl__等属性字段时,无需读取记录的 value 数据,可显著减少磁盘 IO。 - SortList 表 + 仅排序字段:当条件只使用了 sort 字段时,可通过 List 的索引直接判断元素,无需打开记录。
- SortList 表 + 单一排序字段 + 简单二元比较:例如
field >= 1、field == 1,TcaplusDB 走二分查找路径,时延更低。 - 其它情况退化为 逐条解包记录后再判断。
性能优化建议:
- 优先把过滤条件落在 key、index 或 sort 字段上。
- 利用
$.LastAccessTime/__version__做"最近未变更才更新"等惯用模式时,几乎零额外开销。 - 复杂的嵌套字段或数组
CONTAINS条件需要解包整条记录,对单 key 操作影响有限,但用于全表扫描或大规模IndexGet时需谨慎。
7. 注意事项与限制
- 文本最大长度:
condition、operation字符串各自最大 1023 字节。 SetValue限制(TDR):TDR SDK 通过SetValue()设置的复杂类型(结构体、数组)字段不参与条件过滤/数组更新,必须使用SetData();背景及跨实例 TDR 元数据版本兼容详见《TDR 表数据读写与版本兼容性》。- 数组
refer字段(TDR):PUSH/POP 自动维护 refer 字段,业务无需手动同步。 - 路径重叠(PB FieldSet/FieldGet):字段路径集合中不要出现"父子重叠"的路径,如
{"pay", "pay.order.desc"},否则更新顺序与最终结果不可预期;详见《部分字段查询和更新》。 - 多
[]路径:路径中只允许出现一次方括号,不支持map['k1'].sub_map['k2']或arr1[0].arr2[1]。 - 关键字冲突:字段名命中 SQL 关键字时使用反引号转义。
- 删除不存在的元素不报错:POP 给定的下标范围超出数组大小时,超出部分静默忽略。
- 大小写:内建函数名不区分大小写;
LIKE默认忽略大小写。 - Batch 命令的版本要求:所有 Batch 类的
condition/operation需要 SDK 与 TcapSvr 同时 ≥ 3.64.0;当前公开 SDK 上限 3.55.0 暂未开放。 ListDeleteAll暂不支持condition,请改用ListGetAll+ 业务侧筛选 +ListDelete。
8. 相关文档
- 《条件过滤和更新语法说明》:完整的类 SQL 文本语法定义。
- 《批量操作接口的相关说明》:Batch 类请求的拆分、聚合、分包行为。
- 《PB 表的部分字段查询和更新》:
FieldGet/FieldSet/FieldUpdate的字段路径集合用法。 - 《Schema-free》:基于 PB map +
FieldSet的 schema-free 实践。 - 各 SDK 的
01C++_SDK/03条件过滤和更新说明.md:SDK 独有的接口签名与代码示例。