Protobuf-представление таблиц
В данном разделе описано использование протокола передачи структурированных данных protobuf для работы с таблицами в С++ API.
Введение
С++ API позволяет использовать классы (сообщения) protobuf для чтения и записи таблиц как клиентом, так и внутри джоба.
Рекомендуется использовать версию proto2. В случае использования proto3 могут возникнуть ошибки: например, при попытке записать 0
в поле required=%true
.
Схема работы
Пользователь описывает proto-структуру в файле .proto
. Proto-структура может быть размечена различными флагами. Флаги влияют на то, как YTsaurus будет формировать или интерпретировать поля, указанные внутри сообщений.
Примитивные типы
Для работы с типом данных YTsaurus из первой колонки можно использовать соответствующий protobuf-тип из второй колонки в таблице ниже.
YTsaurus | Protobuf |
---|---|
string , utf8 |
string , bytes |
int{8,16,32,64} |
int{32,64} , sint{32,64} , sfixed{32,64} |
uint{8,16,32,64} |
uint{32,64} , fixed{32,64} |
double |
double , float |
bool |
bool |
date , datetime , timestamp |
uint{32,64} , fixed{32,64} |
interval |
int{32,64} , sint{32,64} , sfixed{32,64} |
Если диапазон целочисленного YTsaurus-типа не соответствует диапазону protobuf-типа, выполняется проверка в момент кодирования или декодирования.
Свойства optional / required
Свойства optional
/ required
в protobuf не обязаны соответствовать опциональности колонок в YT. Система всегда делает проверку в момент кодирования или декодирования protobuf-сообщений.
Например, если колонка foo
в YTsaurus имеет тип int64
(required=%true
), для её представления можно использовать поле:
optional int64 foo = 42;
Ошибок не будет, пока все protobuf-сообщения, которые записываются в таблицу, имеют заполненное поле foo
.
Вложенные сообщения
Исторически все вложенные protobuf-структуры записываются в YTsaurus в виде байтовой строки, хранящей сериализованное представление вложенной структуры.
Вложенные сообщения достаточно эффективны, но не позволяют удобно представлять значения в веб-интерфейсе или работать с ними другими способами (без помощи protobuf).
Этот способ также можно указать явно, выставив специальный флаг в поле:
optional TEmbeddedMessage UrlRow_1 = 1 [(NYT.flags) = SERIALIZATION_PROTOBUF];
Можно указать альтернативный флаг, тогда YTsaurus будет относить поле к типу struct
:
optional TEmbeddedMessage ColumnName = 1 [(NYT.flags) = SERIALIZATION_YT];
Для использования вложенных сообщений требуется, чтобы:
- у таблицы была задана схема;
- соответствующая колонка (в примере:
ColumnName
) имела YTsaurus-тип struct; - поля типа
struct
соответствовали полям вложенного сообщения (в примере:TEmbeddedMessage
).
Важно
Флаг во вложенных сообщениях не наследуется по умолчанию. Если для поля с типом T
выставлен флаг SERIALIZATION_YT
, то для структур, вложенных в T
, поведение по умолчанию все равно будет соответствовать флагу SERIALIZATION_PROTOBUF
.
Повторяющиеся поля
Чтобы работать с повторяющимися (repeated) полями, нужно явно указать флаг SERIALIZATION_YT
:
repeated TType ListColumn = 1 [(NYT.flags) = SERIALIZATION_YT];
В YT такое поле будет иметь тип list
. Для использования повторяющихся полей требуется, чтобы:
- у таблицы была задана схема;
- соответствующая колонка (в примере:
ListColumn
) имела YTsaurus-тип list; - элемент YTsaurus-типа
list
соответствовал типу колонки protobuf (в примере:TType
). Это может быть примитивный тип или вложенное сообщение.
Важно
Флаг SERIALIZATION_PROTOBUF
для повторяющихся полей не поддерживается.
Поля oneof
По умолчанию поля внутри oneof
-группы соответствуют YTsaurus-типу variant. Например, сообщение ниже будет соответствовать структуре с полями x
типа int64
и my_oneof
типа variant<y: string, z: bool>
:
message TMessage {
optional int64 x = 1;
oneof my_oneof {
string y = 2;
bool z = 3;
}
}
При выводе схемы с помощью CreateTableSchema<T>()
будет выведен аналогичный тип.
Чтобы группы oneof
соответствовали полям структуры, в которой они описаны, используйте флаг (NYT.oneof_flags) = SEPARATE_FIELDS
:
message TMessage {
optional int64 x = 1;
oneof my_oneof {
option (NYT.oneof_flags) = SEPARATE_FIELDS;
string y = 2;
bool z = 3;
}
}
Этому сообщению будет соответствовать структура с опциональными полями x
, y
и z
.
Поля map
Существует 4 варианта отображения такого поля в столбец в таблице. См. пример ниже:
message TValue {
optional int64 x = 1;
}
message TMessage {
map<string, TValue> map_field = 1 [(NYT.flags) = SERIALIZATION_YT];
}
Поле map_field
в зависимости от своих флагов может соответствовать:
- списку структур с полями
key
типаstring
иvalue
типаstring
, в котором будет лежать сериализованный protobufTValue
(как будто у поляvalue
выставлен флагSERIALIZATION_PROTOBUF
). По умолчанию в этом случае установлен флагMAP_AS_LIST_OF_STRUCTS_LEGACY
. - списку структур с полями
key
типаstring
иvalue
типаStruct<x: Int64>
(как будто у поляvalue
выставлен флагSERIALIZATION_PROTOBUF
). По умолчанию в этом случае установлен флагMAP_AS_LIST_OF_STRUCTS
. - словарю
Dict<String, Struct<x: Int64>>>
: флагMAP_AS_DICT
. - опциональному словарю
Optional<Dict<String, Struct<x: Int64>>>>
: флагMAP_AS_OPTIONAL_DICT
.
Флаги
С помощью флагов можно настроить поведение protobuf. Для этого необходимо подключить библиотечный .proto
файл.
import "yt/yt_proto/yt/formats/extension.proto";
Флаги могут соответствовать сообщениям, oneof
-группам и полям сообщений.
Указывать флаги можно на уровне .proto
файла, сообщения, oneof
-группы и поля сообщения.
SERIALIZATION_YT, SERIALIZATION_PROTOBUF
Поведение этих флагов описано выше.
По умолчанию там, где это актуально, подразумевается SERIALIZATION_PROTOBUF
. Есть возможность поменять флаг для одного сообщения:
message TMyMessage
{
option (NYT.default_field_flags) = SERIALIZATION_YT;
...
}
OTHER_COLUMNS
Флагом OTHER_COLUMNS
можно пометить поле типа bytes
. В это поле помещается YSON-мап, содержащий представления всех полей, которые не описываются другими полями этой protobuf-структуры.
message TMyMessage
{
...
optional bytes OtherColumns = 1 [(NYT.flags) = OTHER_COLUMNS];
...
}
ENUM_STRING / ENUM_INT
Флагами ENUM_STRING
/ ENUM_INT
можно помечать поля типа enum
:
- Если поле помечено
ENUM_INT
, то оно будет сохраняться в колонку в виде целого числа. - Если поле помечено
ENUM_STRING
, то оно будет сохраняться в колонку в виде строки.
По умолчанию подразумевается ENUM_STRING
.
enum Color
{
WHITE = 0;
BLUE = 1;
RED = -1;
}
...
optional Color ColorField = 1 [(NYT.flags) = ENUM_INT];
...
ANY
Флагом ANY
можно помечать поля типа bytes
. Такие поля содержат YSON-представление колонки любого простого типа.
Например, для колонки типа string
можно написать следующий код:
// message.proto
message TMyMessage
{
...
optional bytes AnyColumn = 1 [(NYT.flags) = ANY];
...
}
// main.cpp
TNode node = "Linnaeus";
TMyMessage m;
m.SetAnyColumn(NodeToYsonString(node));