条件过滤和更新

条件过滤和更新包含条件过滤更新两部分内容,后者是指对数组的更新。

1. 版本要求

  • SDK 要求版本大于等于 3.55.0,其中 Batch 类操作则要求 3.64.0 以上的版本(目前最高仅提供3.55.0的SDK,即目前批量操作不支持条件更新和过滤查询)。
  • Client 工具使用条件过滤要求版本大于等于 3.55.0。

2. 哪些命令支持条件更新?

主要的增删改查均支持条件更新,详情见各 API 的参数说明,API 的提炼说明如下

  • 查询请求支持条件过滤,包括
    • Get
    • FieldGet
    • IndexGet(也就是GetByPartKey)
    • Traverse
    • ListGet
    • ListGetAll
    • ListTraverse
  • 更新请求支持条件过滤和数组更新操作,包括
    • Update
    • Replace
    • Delete
    • FieldSet(也就是FieldUpdate)
    • FieldIncrease
    • ListReplace
    • ListDelete
  • 批量查询条件过滤,但要求 3.64.0 以上的版本,包括
    • BatchGet
    • BatchGetByPartKey
    • BatchFieldGet
    • ListGetBatch
  • 更新请求支持条件过滤和数组更新操作,但要求 3.64.0 以上的版本,包括
    • BatchUpdate
    • BatchReplace
    • BatchDelete
    • ListDeleteBatch
    • ListUpdateBatch
    • ListReplaceBatch
  • 两个请求,专门用于操作、查询数组:
    • UpdateItem、ListUpdateItem,用于对数组字段进行 PUSH、SET、POP 操作
    • Query、ListQuery,用于查询部分数组范围,即数组的 GET 操作
  • 历史原因,TDR 协议的 Increase 要求 3.64.0 以上的版本才能支持条件更新,低于 3.64.0 的版本,可以使用 Update 命令,后者可支持字段的更新和自增。
  • 历史原因,UpdateByPartKey、DeletePartKey 要求 3.64.0 以上的版本才能支持条件更新
  • 历史原因,ListDeleteAll 暂不支持条件过滤

更多关于条件过滤和更新的能力支持,请联系Tcaplus。

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. 条件过滤语法

过滤条件是类 SQL 的 WHERE 语句的语法,已支持以下几种过滤能力

  • 比较,如 rank > 1 ,比较符有 >, >=, <, <=, =, ==, !=, <>,在比较的上下文中, = 也是相等比较,而在其他语境下可能是赋值符号 。
  • 逻辑运算,如 rank > 1 AND rank < 10,运算符有 AND, OR, NOT
  • 位运算,仅支持“与”,如 filter & 8,当从低往高的第三位为 1 时,该表达式为 true。
  • CONTAINSNOT CONTAINS,即判断数组是否包含,如 "mailbox CONTAINS(title == \"tcaplus\")" 表示要求 mailbox 包含一个 title 等于 "tcaplus" 的元素。CONTAINS 括号中可以是更复杂的子条件。
  • LIKENOT LIKE,支持对字符串字段模糊匹配,如 "name LIKE 'abc%'",和 MySQL 语法一样,默认忽略大小写,'%'通配任意字符串,'_'匹配任意单字符。
  • INNOT IN,判断某字段值是否满足多个散列的值,如 "name IN ('abc', 'ABC', '123')"
  • 支持嵌套字段,如 mail.titlemailbox[0].titlemap_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: 一个合法的标识名称,在这里是字段名或字段名路径,如 namemail.title
    • number: 整型或浮点数,不支持大整数。
    • string: 双引号或者单引号括起来的字符串,支持类 C 语言风格的转义字符,如 "abc\t123"
  • 比较
    • 不同精度的整型或浮点型的数值都是可以相互比较的,这和 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。
  • 关键字
    • 存在一些语法关键字,如 keylikeupdate 等,若存在表的字段名和这些关键字重名,会语法错误,如 "key > 100",不过可以使用引号 ` 把字段名括起来即可规避,如改为"`key` > 100"。
    • 因为 TcaplusDB 兼容 MySQL 协议,原则上关键字和 MySQL 的关键字一样,具体可查询:Keywords and Reserved Words

性能优化建议:

条件过滤的性能跟条件表达式、表的结构定义有关,满足以下规则时,有针对性的性能优化(仅供参考,内部实现可能会调整)

  • 当条件表达式只用到 key 字段(包括主键和 index 字段)和记录的属性字段(如 $.LastAccessTime),仅通过记录的主键或索引即可进行判断,无需从存储引擎读取全量数据。
  • 对于 SortList 表,当条件表达式只使用了 sort 字段,也有性能优化。
  • 对于 SortList 表,表定义的排序字段只有一个,且条件表达式是简单的二元比较(如 "field >= 1""field == 1" 等),使用二分查找,有更好的性能优化。

5. 数组操作语法

TcaplusDB 提供对数组的命令式的操作,具备如下能力

  • PUSH 操作:在数组指定位置插入新的元素数据。
  • SET 操作:修改数组指定位置的元素数据。
  • POP 操作:删除数组中某些下标范围或者满足某些条件的元素。
  • GET 查询:指定记录的 key,查询数组返回数组中某些下标范围或者满足某些条件的元素(即仅记录的局部数据)。

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 个元素。

6. 注意事项

  • 参数 conditionoperation 文本最大长度是1023。
  • TDR 表的条件过滤和更新依赖于 TDR 的解包(Unpack)能力,而 TDR SDK 支持两种方式写入数据记录,即 Record.SetValue() 和 Record.SetData(),这里不支持 SetValue() 写入的复杂数据类型(结构体、数组)。
  • TDR 表中数组字段有对应的 refer 字段(数组的实际大小),对数组的 PUSH、POP 操作会自动修改这个 refer 字段。

results matching ""

    No results matching ""