部分字段查询和更新

部分字段查询和更新

Pb对象是嵌套树结构,完整的Pb message可能会较大,tcaplus可支持对部分字段或嵌套字段的查询和更新,如”timekey”, “pay.order.desc”, “str_map[‘key’].desc”。

部分字段接口的更新范围也会影响新老proto共存时的数据结果。叶子字段更新、父message替换和完整message写入的兼容性差异见《PB 表数据读写与版本兼容性》。

示例表:

message tb_online3 {
    option(tcaplusservice.tcaplus_primary_key) = "openid";

    message order_info {
        int32 oid = 1;
        string desc = 2;
    }

    message pay_info {
        double total_money = 1;
        uint64 pay_times = 2;
        order_info order = 3;

        int32 pid = 4;
        string desc = 5;
    }

    int32 openid = 1;
    uint32 tconndid = 2;
    string timekey = 3;
    repeated int64 lockid = 4;
    pay_info pay = 5;
    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;

    map<string, string> str_str = 11;
}

部分字段查询

Pb表支持部分字段查询,见FieldGet接口,大致参数如下:

  • 入参&出参:pb message,message中的key,作为输入;查询成功之后,返回的数据会回填到这个message中作为输出。
  • 入参:field_paths,指定的要更新的字段集合,如{“timekey”, “pay.order.desc”, “str_map[‘key’].desc”}。
  • 入参:condition,条件过滤,仅当条件满足时才返回,否则会返回条件不匹配的错误码COMMON_ERR_CONDITION_NOT_MATCHED。

支持以下类型的字段:

  • 一级字段,如”timekey”、“pay”、“desc_array”。
  • 嵌套字段,如”pay.order.desc”。
  • Map的元素的嵌套字段,如”int_map[1001]”、“str_str[‘key’],对于”str_map[‘key’].desc”,若map的key不存在则报错COMMON_ERR_ELEMENT_NOT_EXIST。
  • 数组的嵌套字段,如”pay_array[0]”、“pay_array[0].order.desc”,若数组下标不存在则报错。
  • 数组范围,如”pay_array[0-100]”、“pay_array[0- -1]“,其中-1表示直到末尾。
  • 一趟请求,可支持对多个字段进行查询,如{“timekey”, “pay.order.desc”, “str_map[‘key’].desc”}。
  • 但是多字段不能有路径重叠,如{“pay”, “pay.order.desc”},可能得不到预期结果。

字段路径的总结,使用点号(.)表示嵌套字段,使用方括号([])表示数组索引和map key,不支持路径中使用多个方括号([]),如”map[‘key’].a.sub_map[‘sub_key’]”。

List表的ListGet也可以只查询元素的部分字段(C++ SDK通过ListGetRequest::m_setPaths指定),但只支持点号分隔的一级字段和嵌套字段,如”gamesvrid”、“pay.pay_times”,不支持map key和数组下标,见《[List表]查询列表中指定位置的元素》。

部分字段更新

Pb表支持部分字段更新,见C++ SDK的FieldSet接口,Go SDK的FieldUpdate接口。

FieldSet或FieldUpdate的行为如下:

  • 入参&出参:pb message,message中的key和包含的增量数据,作为输入;更新成功之后,返回的数据会回填到这个message中。
  • 入参:field_paths,指定的要更新的字段集合,如{“timekey”, “pay.order.desc”, “str_map[‘key’].desc”}。
  • 入参:operation,额外捎带的文本描述的操作,如”pay.pay_times += 1”。
  • 入参:condition,条件过滤,仅当条件满足时才更新,否则会返回条件不匹配的错误码COMMON_ERR_CONDITION_NOT_MATCHED。

部分字段更新,支持以下这些类型的字段:

  • 一级字段的更新,如”timekey”、“pay”、“desc_array”。
  • 嵌套字段,如”pay.order.desc”。
  • Map的元素的嵌套字段,如”int_map[1001]”、“str_map[‘key’].desc”、“str_str[‘key’],若map的key不存在则创建之。
  • Map元素的插入,如”PUSH int_map[1001]“,若map的key已存在则报错。
  • Map元素的删除,如”POP int_map[1001]“,若map的key不存在,也会返回成功。
  • 一趟请求,可支持对多个字段进行更新,会同时生效,如{“timekey”, “pay.order.desc”, “str_map[‘key’].desc”}。
  • 但是多字段更新不能有路径重叠,如{“pay”, “pay.order.desc”},不会报错,但因为更新顺序问题可能得不到预期结果。

字段路径决定覆盖范围。例如,更新pay.order.desc只修改叶子字段;更新pay则替换整个父message。新老proto共存时,父message中未被旧proto识别的字段能否保留,取决于提交的pay是否携带对应UnknownField。

因为对Map元素的更新,可以支持一种类Schema-free的用法,见《Schema-free》。

不支持像map元素一样,对数组元素的更新(即repeated字段):

  • 不支持数组元素的嵌套字段,如”pay_array[0]”、“pay_array[0].desc”,只支持数组字段的整体更新(即”pay_array”)。
  • 若要更新数组,只能通过operation语句对数组元素进行PUSH、SET、POP操作,文本语法见《条件过滤和更新语法说明》,能力与限制见《条件过滤和更新功能》。

字段路径的总结,使用点号(.)表示嵌套字段,使用方括号([])表示map key(不支持数组索引),不支持路径中使用多个方括号([]),如”map[‘key’].a.sub_map[‘sub_key’]”。

记录不存在时创建

默认,当修改的记录不存在时,会报错TXHDB_ERR_RECORD_NOT_EXIST。 若希望记录不存在时,自动创建,可通过接口SetMessageOption设置。

C++SDK相关接口:

    /**
    *    i).MESSAGE_OPTION_ENABLE_SET_NOT_EXIST = 9, API中设置允许FieldSet操作不存在的记录时创建之,取值0不允许,取值1允许
    */
    int SetMessageOption(const ::google::protobuf::Message &msg, int32_t item, const std::string &option);

GoSDK相关接口:

const (
    TcaplusFlagInsertRecordIfNotExist           int32 = 16 //PB的FieldUpdate使用,数据不存在则插入
)

func (req *pbFieldUpdateRequest) SetFlags(flag int32) int

ResultFlag

更新返回之后,默认总是把请求的key和增量数据回填到原message(入参&出参)。 而有些场景下,需要返回其他数据,如更新前的旧数据,接口SetMessageOption支持设置返回数据。

C++SDK相关接口:

    /**
    *    j).MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS = 10, 用户显式设置请求执行成功时的响应标志,会覆盖API的默认配置
    *       option取值有    "0":表示只需返回操作执行成功与否
    *                       "1":表示返回与请求字段一致
    *                       "2":表示须返回变更记录的所有字段最新数据
    *                       "3":表示须返回变更记录的所有字段旧数据
    *       其中,对于FieldSet操作,不设置该选项,总是返回和请求一致的数据
    *    k).MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL = 11, 用户显式设置请求执行失败时的响应标志,会覆盖API的默认配置
    *       option取值有    "0":表示只需返回操作执行成功与否
    *                       "1":表示返回与请求字段一致
    *                       "2":表示须返回变更记录的所有字段最新数据
    *                       "3":表示须返回变更记录的所有字段旧数据
    *       其中,对于FieldSet操作,不设置该选项,总是返回和请求一致的数据
    */
    int SetMessageOption(const ::google::protobuf::Message &msg, int32_t item, const std::string &option);

GoSDK相关接口:

const (
    TcaplusResultFlagNoValue         byte = 0
    TcaplusResultFlagSameWithRequest byte = 1
    TcaplusResultFlagAllNewValue     byte = 2
    TcaplusResultFlagAllOldValue     byte = 3
)

func (req *pbFieldUpdateRequest) SetResultFlagForSuccess(result_flag byte) int
func (req *pbFieldUpdateRequest) SetResultFlagForFail(result_flag byte) int

当option取值2、3时,返回的是记录的所有字段数据(非部分修改字段),若完整记录数据较大时,高频操作会占用较大的网路带宽。

List表部分字段更新(ListReplace)

PB List表的ListReplace(C++ SDK的ListReplace接口,Go SDK的DoListReplace接口)也支持部分字段更新,对标Generic表的FieldSet(Go SDK为FieldUpdate)。

语义说明:接口名称虽然是ListReplace,实际是ListUpdate的语义:

  • 必须指定元素的index,且该元素必须已存在。key对应的List不存在时返回TXHDB_ERR_RECORD_NOT_EXIST,index不存在时返回SVR_ERR_FAIL_INVALID_INDEX。ListReplace不会插入新元素,也不支持记录不存在时创建。
  • 默认(不指定字段路径)是整个message覆盖式更新:用请求中的message替换该元素的全部value字段,message中未设置的字段会被清空。
  • 指定字段路径时(C++ SDK的ListReplaceRequest::m_setPaths,Go SDK的option.PBOpt.FieldNames),只更新这些字段,其余字段保持不变。字段路径的写法与限制同上文《部分字段更新》中的FieldSet/FieldUpdate,如嵌套字段、map元素的更新与PUSH/POP,路径中不能包含key字段。
  • condition在更新前对元素的当前数据进行判断,不满足时返回COMMON_ERR_CONDITION_NOT_MATCHED,元素保持不变。
  • operation是更新之后的后置更新动作:TcaplusDB先完成字段更新(或整个message覆盖),再在更新后的元素上执行operation,二者在同一次请求内完成。operation支持标量字段的+=、-=和数组的PUSH、SET、POP,语法见《条件过滤和更新语法说明》§6,能力与限制见《条件过滤和更新功能》§5。
  • SortList表更新了排序字段时,元素会按更新后的值重新排序。

示例代码见《[PB List表][C++ SDK]更新列表中指定位置的元素》4.2:只更新gamesvrid和pay.pay_times,并在更新后向lockid尾部追加一个元素。

返回数据:与FieldSet默认返回和请求一致的数据不同,ListReplace按以下规则返回(C++ SDK通过SetMessageOption设置MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS、MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL,Go SDK通过option.PBOpt的ResultFlagForSuccess、ResultFlagForFail设置):

  • C++ SDK未设置时,成功返回更新后的完整元素;只关心执行结果时,建议设为”0”不返回数据。Go SDK需要显式设置ResultFlagForSuccess才返回数据。
  • 成功时设为”1”(返回与请求一致)不返回数据;设为”2”返回更新后的完整元素,设为”3”返回更新前的完整元素。
  • 失败时设为”3”:条件不满足、版本冲突、字段更新失败等已读到元素的场景,返回更新前的完整元素;key或index不存在时不返回数据。

返回的都是元素的全部字段,而不是请求中的部分字段。

批量更新不支持部分字段更新:ListUpdateBatch(C++ SDK的ListBatchUpdate接口,Go SDK的DoListReplaceBatch接口)暂不支持部分字段更新,每个元素总是整个message覆盖;需要部分更新时,请逐条调用ListReplace。

版本要求:部分字段更新需C++ PB SDK 3.55.0 SP06(3.55.0.209070)及以上,或Go SDK v0.6.36及以上;服务端须为Tcapsvr 3.73.0 2026-10-10以后的版本,旧版后端需要先升级再使用该功能。

各SDK的接口说明见《[PB List表][C++ SDK]更新列表中指定位置的元素》、《[PB List表][Go SDK]更新列表中指定位置的元素》。